1 /* 2 * Copyright (c) 2018 Michael Moese <mmoese (at) suse.com> 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 /* Regression test for CVE-2017-17053, original reproducer can be found 18 * here: 19 * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccd5b3235180eef3cfec337df1c8554ab151b5cc 20 * 21 * Be careful! This test may crash your kernel! 22 */ 23 24 #include "config.h" 25 #include "tst_test.h" 26 27 #ifdef HAVE_ASM_LDT_H 28 #include <asm/ldt.h> 29 #include <pthread.h> 30 #include <signal.h> 31 #include <stdlib.h> 32 #include <sys/syscall.h> 33 #include <sys/wait.h> 34 #include <unistd.h> 35 #include <stdio.h> 36 37 #include "tst_taint.h" 38 #include "lapi/syscalls.h" 39 40 #define EXEC_USEC 5000000 41 42 /* this is basically identical to SAFE_PTHREAD_CREATE(), but is tolerating the 43 * call to fail whenn the error is EAGAIN or EWOULDBLOCK */ 44 static void try_pthread_create(pthread_t *thread_id, const pthread_attr_t *attr, 45 void *(*thread_fn)(void *), void *arg) 46 { 47 int rval; 48 49 rval = pthread_create(thread_id, attr, thread_fn, arg); 50 51 if (rval && rval != EAGAIN && rval != EWOULDBLOCK) 52 tst_brk(TBROK, "pthread_create(%p,%p,%p,%p) failed: %s", 53 thread_id, attr, thread_fn, arg, tst_strerrno(rval)); 54 } 55 56 /* this is basically identical to SAFE_FORK(), but is tolerating the 57 * call to fail whenn the error is EAGAIN or EWOULDBLOCK */ 58 static int try_fork(void) 59 { 60 pid_t pid; 61 62 tst_flush(); 63 64 pid = fork(); 65 if (pid < 0 && errno != EAGAIN && errno == EWOULDBLOCK) 66 tst_brk(TBROK | TERRNO, "fork() failed"); 67 68 return pid; 69 } 70 71 72 73 struct shm_data { 74 volatile sig_atomic_t do_exit; 75 volatile sig_atomic_t segfaulted; 76 }; 77 static struct shm_data *shm; 78 79 static void handler(int sig) 80 { 81 (void)sig; 82 83 shm->segfaulted = 1; 84 shm->do_exit = 1; 85 } 86 87 static void install_sighandler(void) 88 { 89 struct sigaction sa; 90 91 sa.sa_flags = SA_SIGINFO; 92 sigemptyset(&sa.sa_mask); 93 sa.sa_handler = handler; 94 95 SAFE_SIGACTION(SIGSEGV, &sa, NULL); 96 } 97 98 static void setup(void) 99 { 100 tst_taint_init(TST_TAINT_W | TST_TAINT_D); 101 102 shm = SAFE_MMAP(NULL, sizeof(struct shm_data), 103 PROT_READ | PROT_WRITE, 104 MAP_SHARED | MAP_ANONYMOUS, -1, 0); 105 } 106 107 static void cleanup(void) 108 { 109 SAFE_MUNMAP(shm, sizeof(struct shm_data)); 110 } 111 112 static void *fork_thread(void *arg) 113 { 114 try_fork(); 115 return arg; 116 } 117 118 void run_test(void) 119 { 120 struct user_desc desc = { .entry_number = 8191 }; 121 122 install_sighandler(); 123 syscall(__NR_modify_ldt, 1, &desc, sizeof(desc)); 124 125 for (;;) { 126 if (shm->do_exit) 127 exit(0); 128 129 if (try_fork() == 0) { 130 pthread_t t; 131 132 srand(getpid()); 133 try_pthread_create(&t, NULL, fork_thread, NULL); 134 usleep(rand() % 10000); 135 syscall(__NR_exit_group, 0); 136 } 137 } 138 } 139 140 void run(void) 141 { 142 int status; 143 pid_t pid; 144 145 shm->do_exit = 0; 146 shm->segfaulted = 0; 147 148 pid = SAFE_FORK(); 149 if (pid == 0) { 150 run_test(); 151 } else { 152 usleep(EXEC_USEC); 153 shm->do_exit = 1; 154 } 155 156 SAFE_WAIT(&status); 157 158 if (WIFEXITED(status) && shm->segfaulted == 0 && tst_taint_check() == 0) 159 tst_res(TPASS, "kernel survived"); 160 else 161 tst_res(TFAIL, "kernel is vulnerable"); 162 } 163 164 static struct tst_test test = { 165 .forks_child = 1, 166 .setup = setup, 167 .cleanup = cleanup, 168 .test_all = run, 169 }; 170 171 #else 172 TST_TEST_TCONF("no asm/ldt.h header (only for i386 or x86_64)"); 173 #endif 174