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