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
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
57TEST_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
80TEST(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 */
144char glob_sel;
145int nr_syscalls_emulated;
146int si_code;
147int si_errno;
148
149static 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
161TEST(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
216TEST_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
259TEST(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
287TEST(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
310TEST_HARNESS_MAIN