selftests: Add kselftest for syscall user dispatch
[linux-2.6-microblaze.git] / tools / testing / selftests / syscall_user_dispatch / sud_test.c
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