Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
65TEST_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
88static 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
95static 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
103TEST(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 */
164char glob_sel;
165int nr_syscalls_emulated;
166int si_code;
167int si_errno;
168unsigned long syscall_addr;
169
170static 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
197int 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
210TEST(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
255TEST_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
298TEST(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
326TEST(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
349static 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
366TEST(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
380TEST_HARNESS_MAIN