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