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