You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
3.2 KiB

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2018 Michael Moese <mmoese@suse.com>
*/
/* 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 <asm/ldt.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#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,
.tags = (const struct tst_tag[]) {
{"linux-git", "ccd5b3235180"},
{"CVE", "2017-17053"},
{}
}
};
#else
TST_TEST_TCONF("no asm/ldt.h header (only for i386 or x86_64)");
#endif