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