~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/syscall_user_dispatch/sud_test.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php