1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2020 Collabora Ltd.
5 * Test code for syscall user dispatch
10 #include <sys/sysinfo.h>
11 #include <sys/syscall.h>
14 #include <asm/unistd.h>
15 #include "../kselftest_harness.h"
17 #ifndef PR_SET_SYSCALL_USER_DISPATCH
18 # define PR_SET_SYSCALL_USER_DISPATCH 59
19 # define PR_SYS_DISPATCH_OFF 0
20 # define PR_SYS_DISPATCH_ON 1
23 #ifndef SYS_USER_DISPATCH
24 # define SYS_USER_DISPATCH 2
28 # define MAGIC_SYSCALL_1 (__NR_syscalls + 1) /* Bad Linux syscall number */
30 # define MAGIC_SYSCALL_1 (0xff00) /* Bad Linux syscall number */
33 #define SYSCALL_DISPATCH_ON(x) ((x) = 1)
34 #define SYSCALL_DISPATCH_OFF(x) ((x) = 0)
38 * - dispatch_trigger_sigsys: Verify if PR_SET_SYSCALL_USER_DISPATCH is
39 * able to trigger SIGSYS on a syscall.
41 * - bad_selector: Test that a bad selector value triggers SIGSYS with
44 * - bad_prctl_param: Test that the API correctly rejects invalid
47 * - dispatch_and_return: Test that a syscall is selectively dispatched
48 * to userspace depending on the value of selector.
50 * - disable_dispatch: Test that the PR_SYS_DISPATCH_OFF correctly
51 * disables the dispatcher
53 * - direct_dispatch_range: Test that a syscall within the allowed range
54 * can bypass the dispatcher.
57 TEST_SIGNAL(dispatch_trigger_sigsys, SIGSYS)
66 ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &sel);
68 TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
71 SYSCALL_DISPATCH_ON(sel);
76 TH_LOG("Unreachable!");
87 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0, 0, &sel);
88 ASSERT_EQ(EINVAL, errno);
90 /* PR_SYS_DISPATCH_OFF */
91 op = PR_SYS_DISPATCH_OFF;
94 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x1, 0x0, 0);
95 EXPECT_EQ(EINVAL, errno);
98 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0xff, 0);
99 EXPECT_EQ(EINVAL, errno);
102 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0x0, &sel);
103 EXPECT_EQ(EINVAL, errno);
105 /* Valid parameter */
107 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0x0, 0x0);
110 /* PR_SYS_DISPATCH_ON */
111 op = PR_SYS_DISPATCH_ON;
113 /* Dispatcher region is bad (offset > 0 && len == 0) */
114 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x1, 0x0, &sel);
115 EXPECT_EQ(EINVAL, errno);
116 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, -1L, 0x0, &sel);
117 EXPECT_EQ(EINVAL, errno);
119 /* Invalid selector */
120 prctl(PR_SET_SYSCALL_USER_DISPATCH, op, 0x0, 0x1, (void *) -1);
121 ASSERT_EQ(EFAULT, errno);
124 * Dispatcher range overflows unsigned long
126 prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 1, -1L, &sel);
127 ASSERT_EQ(EINVAL, errno) {
128 TH_LOG("Should reject bad syscall range");
132 * Allowed range overflows usigned long
134 prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, -1L, 0x1, &sel);
135 ASSERT_EQ(EINVAL, errno) {
136 TH_LOG("Should reject bad syscall range");
141 * Use global selector for handle_sigsys tests, to avoid passing
142 * selector to signal handler
145 int nr_syscalls_emulated;
149 static void handle_sigsys(int sig, siginfo_t *info, void *ucontext)
151 si_code = info->si_code;
152 si_errno = info->si_errno;
154 if (info->si_syscall == MAGIC_SYSCALL_1)
155 nr_syscalls_emulated++;
157 /* In preparation for sigreturn. */
158 SYSCALL_DISPATCH_OFF(glob_sel);
161 TEST(dispatch_and_return)
164 struct sigaction act;
168 nr_syscalls_emulated = 0;
172 memset(&act, 0, sizeof(act));
175 act.sa_sigaction = handle_sigsys;
176 act.sa_flags = SA_SIGINFO;
179 ret = sigaction(SIGSYS, &act, NULL);
182 /* Make sure selector is good prior to prctl. */
183 SYSCALL_DISPATCH_OFF(glob_sel);
185 ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel);
187 TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
190 /* MAGIC_SYSCALL_1 doesn't exist. */
191 SYSCALL_DISPATCH_OFF(glob_sel);
192 ret = syscall(MAGIC_SYSCALL_1);
194 TH_LOG("Dispatch triggered unexpectedly");
197 /* MAGIC_SYSCALL_1 should be emulated. */
198 nr_syscalls_emulated = 0;
199 SYSCALL_DISPATCH_ON(glob_sel);
201 ret = syscall(MAGIC_SYSCALL_1);
202 EXPECT_EQ(MAGIC_SYSCALL_1, ret) {
203 TH_LOG("Failed to intercept syscall");
205 EXPECT_EQ(1, nr_syscalls_emulated) {
206 TH_LOG("Failed to emulate syscall");
208 ASSERT_EQ(SYS_USER_DISPATCH, si_code) {
209 TH_LOG("Bad si_code in SIGSYS");
211 ASSERT_EQ(0, si_errno) {
212 TH_LOG("Bad si_errno in SIGSYS");
216 TEST_SIGNAL(bad_selector, SIGSYS)
219 struct sigaction act;
224 nr_syscalls_emulated = 0;
228 memset(&act, 0, sizeof(act));
231 act.sa_sigaction = handle_sigsys;
232 act.sa_flags = SA_SIGINFO;
235 ret = sigaction(SIGSYS, &act, NULL);
238 /* Make sure selector is good prior to prctl. */
239 SYSCALL_DISPATCH_OFF(glob_sel);
241 ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel);
243 TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
250 /* Even though it is ready to catch SIGSYS, the signal is
251 * supposed to be uncatchable.
255 TH_LOG("Unreachable!");
259 TEST(disable_dispatch)
265 ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &sel);
267 TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
270 /* MAGIC_SYSCALL_1 doesn't exist. */
271 SYSCALL_DISPATCH_OFF(glob_sel);
273 ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0);
275 TH_LOG("Failed to unset syscall user dispatch");
278 /* Shouldn't have any effect... */
279 SYSCALL_DISPATCH_ON(glob_sel);
281 ret = syscall(__NR_sysinfo, &info);
283 TH_LOG("Dispatch triggered unexpectedly");
287 TEST(direct_dispatch_range)
294 * Instead of calculating libc addresses; allow the entire
295 * memory map and lock the selector.
297 ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, -1L, &sel);
299 TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
302 SYSCALL_DISPATCH_ON(sel);
304 ret = sysinfo(&info);
306 TH_LOG("Dispatch triggered unexpectedly");