• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018 Michael Moese <mmoese@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 */
try_pthread_create(pthread_t * thread_id,const pthread_attr_t * attr,void * (* thread_fn)(void *),void * arg)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 */
try_fork(void)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 
handler(int sig)79 static void handler(int sig)
80 {
81 	(void)sig;
82 
83 	shm->segfaulted = 1;
84 	shm->do_exit = 1;
85 }
86 
install_sighandler(void)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 
setup(void)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 
cleanup(void)107 static void cleanup(void)
108 {
109 	SAFE_MUNMAP(shm, sizeof(struct shm_data));
110 }
111 
fork_thread(void * arg)112 static void *fork_thread(void *arg)
113 {
114 	try_fork();
115 	return arg;
116 }
117 
run_test(void)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 
run(void)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