Home | History | Annotate | Download | only in cve
      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