• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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