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 | TEST(dispatch_and_return) |
164 | { |
165 | long ret; |
166 | struct sigaction act; |
167 | sigset_t mask; |
168 | |
169 | glob_sel = 0; |
170 | nr_syscalls_emulated = 0; |
171 | si_code = 0; |
172 | si_errno = 0; |
173 | |
174 | memset(&act, 0, sizeof(act)); |
175 | sigemptyset(&mask); |
176 | |
177 | act.sa_sigaction = handle_sigsys; |
178 | act.sa_flags = SA_SIGINFO; |
179 | act.sa_mask = mask; |
180 | |
181 | ret = sigaction(SIGSYS, &act, NULL); |
182 | ASSERT_EQ(0, ret); |
183 | |
184 | /* Make sure selector is good prior to prctl. */ |
185 | SYSCALL_DISPATCH_OFF(glob_sel); |
186 | |
187 | ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel); |
188 | ASSERT_EQ(0, ret) { |
189 | TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH" ); |
190 | } |
191 | |
192 | /* MAGIC_SYSCALL_1 doesn't exist. */ |
193 | SYSCALL_DISPATCH_OFF(glob_sel); |
194 | ret = syscall(MAGIC_SYSCALL_1); |
195 | EXPECT_EQ(-1, ret) { |
196 | TH_LOG("Dispatch triggered unexpectedly" ); |
197 | } |
198 | |
199 | /* MAGIC_SYSCALL_1 should be emulated. */ |
200 | nr_syscalls_emulated = 0; |
201 | SYSCALL_DISPATCH_ON(glob_sel); |
202 | |
203 | ret = syscall(MAGIC_SYSCALL_1); |
204 | EXPECT_EQ(MAGIC_SYSCALL_1, ret) { |
205 | TH_LOG("Failed to intercept syscall" ); |
206 | } |
207 | EXPECT_EQ(1, nr_syscalls_emulated) { |
208 | TH_LOG("Failed to emulate syscall" ); |
209 | } |
210 | ASSERT_EQ(SYS_USER_DISPATCH, si_code) { |
211 | TH_LOG("Bad si_code in SIGSYS" ); |
212 | } |
213 | ASSERT_EQ(0, si_errno) { |
214 | TH_LOG("Bad si_errno in SIGSYS" ); |
215 | } |
216 | } |
217 | |
218 | TEST_SIGNAL(bad_selector, SIGSYS) |
219 | { |
220 | long ret; |
221 | struct sigaction act; |
222 | sigset_t mask; |
223 | struct sysinfo info; |
224 | |
225 | glob_sel = SYSCALL_DISPATCH_FILTER_ALLOW; |
226 | nr_syscalls_emulated = 0; |
227 | si_code = 0; |
228 | si_errno = 0; |
229 | |
230 | memset(&act, 0, sizeof(act)); |
231 | sigemptyset(&mask); |
232 | |
233 | act.sa_sigaction = handle_sigsys; |
234 | act.sa_flags = SA_SIGINFO; |
235 | act.sa_mask = mask; |
236 | |
237 | ret = sigaction(SIGSYS, &act, NULL); |
238 | ASSERT_EQ(0, ret); |
239 | |
240 | /* Make sure selector is good prior to prctl. */ |
241 | SYSCALL_DISPATCH_OFF(glob_sel); |
242 | |
243 | ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel); |
244 | ASSERT_EQ(0, ret) { |
245 | TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH" ); |
246 | } |
247 | |
248 | glob_sel = -1; |
249 | |
250 | sysinfo(&info); |
251 | |
252 | /* Even though it is ready to catch SIGSYS, the signal is |
253 | * supposed to be uncatchable. |
254 | */ |
255 | |
256 | EXPECT_FALSE(true) { |
257 | TH_LOG("Unreachable!" ); |
258 | } |
259 | } |
260 | |
261 | TEST(disable_dispatch) |
262 | { |
263 | int ret; |
264 | struct sysinfo info; |
265 | char sel = 0; |
266 | |
267 | ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &sel); |
268 | ASSERT_EQ(0, ret) { |
269 | TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH" ); |
270 | } |
271 | |
272 | /* MAGIC_SYSCALL_1 doesn't exist. */ |
273 | SYSCALL_DISPATCH_OFF(glob_sel); |
274 | |
275 | ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0); |
276 | EXPECT_EQ(0, ret) { |
277 | TH_LOG("Failed to unset syscall user dispatch" ); |
278 | } |
279 | |
280 | /* Shouldn't have any effect... */ |
281 | SYSCALL_DISPATCH_ON(glob_sel); |
282 | |
283 | ret = syscall(__NR_sysinfo, &info); |
284 | EXPECT_EQ(0, ret) { |
285 | TH_LOG("Dispatch triggered unexpectedly" ); |
286 | } |
287 | } |
288 | |
289 | TEST(direct_dispatch_range) |
290 | { |
291 | int ret = 0; |
292 | struct sysinfo info; |
293 | char sel = SYSCALL_DISPATCH_FILTER_ALLOW; |
294 | |
295 | /* |
296 | * Instead of calculating libc addresses; allow the entire |
297 | * memory map and lock the selector. |
298 | */ |
299 | ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, -1L, &sel); |
300 | ASSERT_EQ(0, ret) { |
301 | TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH" ); |
302 | } |
303 | |
304 | SYSCALL_DISPATCH_ON(sel); |
305 | |
306 | ret = sysinfo(&info); |
307 | ASSERT_EQ(0, ret) { |
308 | TH_LOG("Dispatch triggered unexpectedly" ); |
309 | } |
310 | } |
311 | |
312 | TEST_HARNESS_MAIN |
313 | |