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