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