cve-2017-17053.c 3.2 KB
Newer Older
M
m00302376 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
// 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