xref: /linux/tools/testing/selftests/syscall_user_dispatch/sud_test.c (revision 78bb43e51b94828b333ab296eabf893d5b439fc2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2020 Collabora Ltd.
4  *
5  * Test code for syscall user dispatch
6  */
7 
8 #define _GNU_SOURCE
9 #include <sys/prctl.h>
10 #include <sys/sysinfo.h>
11 #include <sys/syscall.h>
12 #include <signal.h>
13 #include <stdbool.h>
14 #include <stdlib.h>
15 
16 #include <asm/unistd.h>
17 #include "../kselftest_harness.h"
18 
19 #ifndef PR_SET_SYSCALL_USER_DISPATCH
20 # define PR_SET_SYSCALL_USER_DISPATCH	59
21 # define PR_SYS_DISPATCH_OFF	0
22 # define SYSCALL_DISPATCH_FILTER_ALLOW	0
23 # define SYSCALL_DISPATCH_FILTER_BLOCK	1
24 #endif
25 
26 #ifndef PR_SYS_DISPATCH_EXCLUSIVE_ON
27 # define PR_SYS_DISPATCH_EXCLUSIVE_ON	1
28 # define PR_SYS_DISPATCH_INCLUSIVE_ON	2
29 #endif
30 
31 #ifndef SYS_USER_DISPATCH
32 # define SYS_USER_DISPATCH	2
33 #endif
34 
35 #ifdef __NR_syscalls
36 # define MAGIC_SYSCALL_1 (__NR_syscalls + 1) /* Bad Linux syscall number */
37 #else
38 # define MAGIC_SYSCALL_1 (0xff00)  /* Bad Linux syscall number */
39 #endif
40 
41 #define SYSCALL_DISPATCH_ON(x) ((x) = SYSCALL_DISPATCH_FILTER_BLOCK)
42 #define SYSCALL_DISPATCH_OFF(x) ((x) = SYSCALL_DISPATCH_FILTER_ALLOW)
43 
44 /* Test Summary:
45  *
46  * - dispatch_trigger_sigsys: Verify if PR_SET_SYSCALL_USER_DISPATCH is
47  *   able to trigger SIGSYS on a syscall.
48  *
49  * - bad_selector: Test that a bad selector value triggers SIGSYS with
50  *   si_errno EINVAL.
51  *
52  * - bad_prctl_param: Test that the API correctly rejects invalid
53  *   parameters on prctl
54  *
55  * - dispatch_and_return: Test that a syscall is selectively dispatched
56  *   to userspace depending on the value of selector.
57  *
58  * - disable_dispatch: Test that the PR_SYS_DISPATCH_OFF correctly
59  *   disables the dispatcher
60  *
61  * - direct_dispatch_range: Test that a syscall within the allowed range
62  *   can bypass the dispatcher.
63  */
64 
TEST_SIGNAL(dispatch_trigger_sigsys,SIGSYS)65 TEST_SIGNAL(dispatch_trigger_sigsys, SIGSYS)
66 {
67 	char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
68 	struct sysinfo info;
69 	int ret;
70 
71 	ret = sysinfo(&info);
72 	ASSERT_EQ(0, ret);
73 
74 	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &sel);
75 	ASSERT_EQ(0, ret) {
76 		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
77 	}
78 
79 	SYSCALL_DISPATCH_ON(sel);
80 
81 	sysinfo(&info);
82 
83 	EXPECT_FALSE(true) {
84 		TH_LOG("Unreachable!");
85 	}
86 }
87 
prctl_valid(struct __test_metadata * _metadata,unsigned long op,unsigned long off,unsigned long size,void * sel)88 static void prctl_valid(struct __test_metadata *_metadata,
89 			unsigned long op, unsigned long off,
90 			unsigned long size, void *sel)
91 {
92 	EXPECT_EQ(0, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, sel));
93 }
94 
prctl_invalid(struct __test_metadata * _metadata,unsigned long op,unsigned long off,unsigned long size,void * sel,int err)95 static void prctl_invalid(struct __test_metadata *_metadata,
96 			  unsigned long op, unsigned long off,
97 			  unsigned long size, void *sel, int err)
98 {
99 	EXPECT_EQ(-1, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, sel));
100 	EXPECT_EQ(err, errno);
101 }
102 
TEST(bad_prctl_param)103 TEST(bad_prctl_param)
104 {
105 	char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
106 	int op;
107 
108 	/* Invalid op */
109 	op = -1;
110 	prctl_invalid(_metadata, op, 0, 0, &sel, EINVAL);
111 
112 	/* PR_SYS_DISPATCH_OFF */
113 	op = PR_SYS_DISPATCH_OFF;
114 
115 	/* offset != 0 */
116 	prctl_invalid(_metadata, op, 0x1, 0x0, 0, EINVAL);
117 
118 	/* len != 0 */
119 	prctl_invalid(_metadata, op, 0x0, 0xff, 0, EINVAL);
120 
121 	/* sel != NULL */
122 	prctl_invalid(_metadata, op, 0x0, 0x0, &sel, EINVAL);
123 
124 	/* Valid parameter */
125 	prctl_valid(_metadata, op, 0x0, 0x0, 0x0);
126 
127 	/* PR_SYS_DISPATCH_EXCLUSIVE_ON */
128 	op = PR_SYS_DISPATCH_EXCLUSIVE_ON;
129 
130 	/* Dispatcher region is bad (offset > 0 && len == 0) */
131 	prctl_invalid(_metadata, op, 0x1, 0x0, &sel, EINVAL);
132 	prctl_invalid(_metadata, op, -1L, 0x0, &sel, EINVAL);
133 
134 	/* Invalid selector */
135 	prctl_invalid(_metadata, op, 0x0, 0x1, (void *) -1, EFAULT);
136 
137 	/*
138 	 * Dispatcher range overflows unsigned long
139 	 */
140 	prctl_invalid(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, 1, -1L, &sel, EINVAL);
141 
142 	/*
143 	 * Allowed range overflows usigned long
144 	 */
145 	prctl_invalid(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, -1L, 0x1, &sel, EINVAL);
146 
147 	/* 0 len should fail for PR_SYS_DISPATCH_INCLUSIVE_ON */
148 	prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 1, 0, 0, EINVAL);
149 
150 	/* Range wrap-around should fail */
151 	prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, -1L, 2, 0, EINVAL);
152 
153 	/* Normal range shouldn't fail */
154 	prctl_valid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, 0);
155 
156 	/* Invalid selector */
157 	prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, (void *) -1, EFAULT);
158 }
159 
160 /*
161  * Use global selector for handle_sigsys tests, to avoid passing
162  * selector to signal handler
163  */
164 char glob_sel;
165 int nr_syscalls_emulated;
166 int si_code;
167 int si_errno;
168 unsigned long syscall_addr;
169 
handle_sigsys(int sig,siginfo_t * info,void * ucontext)170 static void handle_sigsys(int sig, siginfo_t *info, void *ucontext)
171 {
172 	si_code = info->si_code;
173 	si_errno = info->si_errno;
174 	syscall_addr = (unsigned long)info->si_call_addr;
175 
176 	if (info->si_syscall == MAGIC_SYSCALL_1)
177 		nr_syscalls_emulated++;
178 
179 	/* In preparation for sigreturn. */
180 	SYSCALL_DISPATCH_OFF(glob_sel);
181 
182 	/*
183 	 * The tests for argument handling assume that `syscall(x) == x`. This
184 	 * is a NOP on x86 because the syscall number is passed in %rax, which
185 	 * happens to also be the function ABI return register.  Other
186 	 * architectures may need to swizzle the arguments around.
187 	 */
188 #if defined(__riscv)
189 /* REG_A7 is not defined in libc headers */
190 # define REG_A7 (REG_A0 + 7)
191 
192 	((ucontext_t *)ucontext)->uc_mcontext.__gregs[REG_A0] =
193 			((ucontext_t *)ucontext)->uc_mcontext.__gregs[REG_A7];
194 #endif
195 }
196 
setup_sigsys_handler(void)197 int setup_sigsys_handler(void)
198 {
199 	struct sigaction act;
200 	sigset_t mask;
201 
202 	memset(&act, 0, sizeof(act));
203 	sigemptyset(&mask);
204 	act.sa_sigaction = handle_sigsys;
205 	act.sa_flags = SA_SIGINFO;
206 	act.sa_mask = mask;
207 	return sigaction(SIGSYS, &act, NULL);
208 }
209 
TEST(dispatch_and_return)210 TEST(dispatch_and_return)
211 {
212 	long ret;
213 
214 	glob_sel = 0;
215 	nr_syscalls_emulated = 0;
216 	si_code = 0;
217 	si_errno = 0;
218 
219 	ASSERT_EQ(0, setup_sigsys_handler());
220 
221 	/* Make sure selector is good prior to prctl. */
222 	SYSCALL_DISPATCH_OFF(glob_sel);
223 
224 	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &glob_sel);
225 	ASSERT_EQ(0, ret) {
226 		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
227 	}
228 
229 	/* MAGIC_SYSCALL_1 doesn't exist. */
230 	SYSCALL_DISPATCH_OFF(glob_sel);
231 	ret = syscall(MAGIC_SYSCALL_1);
232 	EXPECT_EQ(-1, ret) {
233 		TH_LOG("Dispatch triggered unexpectedly");
234 	}
235 
236 	/* MAGIC_SYSCALL_1 should be emulated. */
237 	nr_syscalls_emulated = 0;
238 	SYSCALL_DISPATCH_ON(glob_sel);
239 
240 	ret = syscall(MAGIC_SYSCALL_1);
241 	EXPECT_EQ(MAGIC_SYSCALL_1, ret) {
242 		TH_LOG("Failed to intercept syscall");
243 	}
244 	EXPECT_EQ(1, nr_syscalls_emulated) {
245 		TH_LOG("Failed to emulate syscall");
246 	}
247 	ASSERT_EQ(SYS_USER_DISPATCH, si_code) {
248 		TH_LOG("Bad si_code in SIGSYS");
249 	}
250 	ASSERT_EQ(0, si_errno) {
251 		TH_LOG("Bad si_errno in SIGSYS");
252 	}
253 }
254 
TEST_SIGNAL(bad_selector,SIGSYS)255 TEST_SIGNAL(bad_selector, SIGSYS)
256 {
257 	long ret;
258 	struct sigaction act;
259 	sigset_t mask;
260 	struct sysinfo info;
261 
262 	glob_sel = SYSCALL_DISPATCH_FILTER_ALLOW;
263 	nr_syscalls_emulated = 0;
264 	si_code = 0;
265 	si_errno = 0;
266 
267 	memset(&act, 0, sizeof(act));
268 	sigemptyset(&mask);
269 
270 	act.sa_sigaction = handle_sigsys;
271 	act.sa_flags = SA_SIGINFO;
272 	act.sa_mask = mask;
273 
274 	ret = sigaction(SIGSYS, &act, NULL);
275 	ASSERT_EQ(0, ret);
276 
277 	/* Make sure selector is good prior to prctl. */
278 	SYSCALL_DISPATCH_OFF(glob_sel);
279 
280 	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &glob_sel);
281 	ASSERT_EQ(0, ret) {
282 		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
283 	}
284 
285 	glob_sel = -1;
286 
287 	sysinfo(&info);
288 
289 	/* Even though it is ready to catch SIGSYS, the signal is
290 	 * supposed to be uncatchable.
291 	 */
292 
293 	EXPECT_FALSE(true) {
294 		TH_LOG("Unreachable!");
295 	}
296 }
297 
TEST(disable_dispatch)298 TEST(disable_dispatch)
299 {
300 	int ret;
301 	struct sysinfo info;
302 	char sel = 0;
303 
304 	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &sel);
305 	ASSERT_EQ(0, ret) {
306 		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
307 	}
308 
309 	/* MAGIC_SYSCALL_1 doesn't exist. */
310 	SYSCALL_DISPATCH_OFF(glob_sel);
311 
312 	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0);
313 	EXPECT_EQ(0, ret) {
314 		TH_LOG("Failed to unset syscall user dispatch");
315 	}
316 
317 	/* Shouldn't have any effect... */
318 	SYSCALL_DISPATCH_ON(glob_sel);
319 
320 	ret = syscall(__NR_sysinfo, &info);
321 	EXPECT_EQ(0, ret) {
322 		TH_LOG("Dispatch triggered unexpectedly");
323 	}
324 }
325 
TEST(direct_dispatch_range)326 TEST(direct_dispatch_range)
327 {
328 	int ret = 0;
329 	struct sysinfo info;
330 	char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
331 
332 	/*
333 	 * Instead of calculating libc addresses; allow the entire
334 	 * memory map and lock the selector.
335 	 */
336 	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, -1L, &sel);
337 	ASSERT_EQ(0, ret) {
338 		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
339 	}
340 
341 	SYSCALL_DISPATCH_ON(sel);
342 
343 	ret = sysinfo(&info);
344 	ASSERT_EQ(0, ret) {
345 		TH_LOG("Dispatch triggered unexpectedly");
346 	}
347 }
348 
test_range(struct __test_metadata * _metadata,unsigned long op,unsigned long off,unsigned long size,bool dispatch)349 static void test_range(struct __test_metadata *_metadata,
350 		       unsigned long op, unsigned long off,
351 		       unsigned long size, bool dispatch)
352 {
353 	nr_syscalls_emulated = 0;
354 	SYSCALL_DISPATCH_OFF(glob_sel);
355 	EXPECT_EQ(0, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, &glob_sel));
356 	SYSCALL_DISPATCH_ON(glob_sel);
357 	if (dispatch) {
358 		EXPECT_EQ(syscall(MAGIC_SYSCALL_1), MAGIC_SYSCALL_1);
359 		EXPECT_EQ(nr_syscalls_emulated, 1);
360 	} else {
361 		EXPECT_EQ(syscall(MAGIC_SYSCALL_1), -1);
362 		EXPECT_EQ(nr_syscalls_emulated, 0);
363 	}
364 }
365 
TEST(dispatch_range)366 TEST(dispatch_range)
367 {
368 	ASSERT_EQ(0, setup_sigsys_handler());
369 	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, true);
370 	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr, 1, false);
371 	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr-100, 200, false);
372 	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr+1, 100, true);
373 	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr-100, 100, true);
374 	test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr, 1, true);
375 	test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr-1, 1, false);
376 	test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr+1, 1, false);
377 	SYSCALL_DISPATCH_OFF(glob_sel);
378 }
379 
380 TEST_HARNESS_MAIN
381