/* * Copyright (c) 2018 Michael Moese * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Regression test for CVE-2017-17053, original reproducer can be found * here: * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccd5b3235180eef3cfec337df1c8554ab151b5cc * * Be careful! This test may crash your kernel! */ #include "config.h" #include "tst_test.h" #ifdef HAVE_ASM_LDT_H #include #include #include #include #include #include #include #include #include "tst_taint.h" #include "lapi/syscalls.h" #define EXEC_USEC 5000000 /* this is basically identical to SAFE_PTHREAD_CREATE(), but is tolerating the * call to fail whenn the error is EAGAIN or EWOULDBLOCK */ static void try_pthread_create(pthread_t *thread_id, const pthread_attr_t *attr, void *(*thread_fn)(void *), void *arg) { int rval; rval = pthread_create(thread_id, attr, thread_fn, arg); if (rval && rval != EAGAIN && rval != EWOULDBLOCK) tst_brk(TBROK, "pthread_create(%p,%p,%p,%p) failed: %s", thread_id, attr, thread_fn, arg, tst_strerrno(rval)); } /* this is basically identical to SAFE_FORK(), but is tolerating the * call to fail whenn the error is EAGAIN or EWOULDBLOCK */ static int try_fork(void) { pid_t pid; tst_flush(); pid = fork(); if (pid < 0 && errno != EAGAIN && errno == EWOULDBLOCK) tst_brk(TBROK | TERRNO, "fork() failed"); return pid; } struct shm_data { volatile sig_atomic_t do_exit; volatile sig_atomic_t segfaulted; }; static struct shm_data *shm; static void handler(int sig) { (void)sig; shm->segfaulted = 1; shm->do_exit = 1; } static void install_sighandler(void) { struct sigaction sa; sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); sa.sa_handler = handler; SAFE_SIGACTION(SIGSEGV, &sa, NULL); } static void setup(void) { tst_taint_init(TST_TAINT_W | TST_TAINT_D); shm = SAFE_MMAP(NULL, sizeof(struct shm_data), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); } static void cleanup(void) { SAFE_MUNMAP(shm, sizeof(struct shm_data)); } static void *fork_thread(void *arg) { try_fork(); return arg; } void run_test(void) { struct user_desc desc = { .entry_number = 8191 }; install_sighandler(); syscall(__NR_modify_ldt, 1, &desc, sizeof(desc)); for (;;) { if (shm->do_exit) exit(0); if (try_fork() == 0) { pthread_t t; srand(getpid()); try_pthread_create(&t, NULL, fork_thread, NULL); usleep(rand() % 10000); syscall(__NR_exit_group, 0); } } } void run(void) { int status; pid_t pid; shm->do_exit = 0; shm->segfaulted = 0; pid = SAFE_FORK(); if (pid == 0) { run_test(); } else { usleep(EXEC_USEC); shm->do_exit = 1; } SAFE_WAIT(&status); if (WIFEXITED(status) && shm->segfaulted == 0 && tst_taint_check() == 0) tst_res(TPASS, "kernel survived"); else tst_res(TFAIL, "kernel is vulnerable"); } static struct tst_test test = { .forks_child = 1, .setup = setup, .cleanup = cleanup, .test_all = run, }; #else TST_TEST_TCONF("no asm/ldt.h header (only for i386 or x86_64)"); #endif