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