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