File Index
Files and directories
Sign in
FILE

sud_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/syscall_user_dispatch/sud_test.c 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#include <stdbool.h>
 14#include <stdlib.h>
 15
 16#include <asm/unistd.h>
 17#include "kselftest_harness.h"
 18
 19#ifndef PR_SET_SYSCALL_USER_DISPATCH
 20# define PR_SET_SYSCALL_USER_DISPATCH	59
 21# define PR_SYS_DISPATCH_OFF	0
 22# define SYSCALL_DISPATCH_FILTER_ALLOW	0
 23# define SYSCALL_DISPATCH_FILTER_BLOCK	1
 24#endif
 25
 26#ifndef PR_SYS_DISPATCH_EXCLUSIVE_ON
 27# define PR_SYS_DISPATCH_EXCLUSIVE_ON	1
 28# define PR_SYS_DISPATCH_INCLUSIVE_ON	2
 29#endif
 30
 31#ifndef SYS_USER_DISPATCH
 32# define SYS_USER_DISPATCH	2
 33#endif
 34
 35#ifdef __NR_syscalls
 36# define MAGIC_SYSCALL_1 (__NR_syscalls + 1) /* Bad Linux syscall number */
 37#else
 38# define MAGIC_SYSCALL_1 (0xff00)  /* Bad Linux syscall number */
 39#endif
 40
 41#define SYSCALL_DISPATCH_ON(x) ((x) = SYSCALL_DISPATCH_FILTER_BLOCK)
 42#define SYSCALL_DISPATCH_OFF(x) ((x) = SYSCALL_DISPATCH_FILTER_ALLOW)
 43
 44/* Test Summary:
 45 *
 46 * - dispatch_trigger_sigsys: Verify if PR_SET_SYSCALL_USER_DISPATCH is
 47 *   able to trigger SIGSYS on a syscall.
 48 *
 49 * - bad_selector: Test that a bad selector value triggers SIGSYS with
 50 *   si_errno EINVAL.
 51 *
 52 * - bad_prctl_param: Test that the API correctly rejects invalid
 53 *   parameters on prctl
 54 *
 55 * - dispatch_and_return: Test that a syscall is selectively dispatched
 56 *   to userspace depending on the value of selector.
 57 *
 58 * - disable_dispatch: Test that the PR_SYS_DISPATCH_OFF correctly
 59 *   disables the dispatcher
 60 *
 61 * - direct_dispatch_range: Test that a syscall within the allowed range
 62 *   can bypass the dispatcher.
 63 */
 64
 65TEST_SIGNAL(dispatch_trigger_sigsys, SIGSYS)
 66{
 67	char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
 68	struct sysinfo info;
 69	int ret;
 70
 71	ret = sysinfo(&info);
 72	ASSERT_EQ(0, ret);
 73
 74	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &sel);
 75	ASSERT_EQ(0, ret) {
 76		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
 77	}
 78
 79	SYSCALL_DISPATCH_ON(sel);
 80
 81	sysinfo(&info);
 82
 83	EXPECT_FALSE(true) {
 84		TH_LOG("Unreachable!");
 85	}
 86}
 87
 88static void prctl_valid(struct __test_metadata *_metadata,
 89			unsigned long op, unsigned long off,
 90			unsigned long size, void *sel)
 91{
 92	EXPECT_EQ(0, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, sel));
 93}
 94
 95static void prctl_invalid(struct __test_metadata *_metadata,
 96			  unsigned long op, unsigned long off,
 97			  unsigned long size, void *sel, int err)
 98{
 99	EXPECT_EQ(-1, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, sel));
100	EXPECT_EQ(err, errno);
101}
102
103TEST(bad_prctl_param)
104{
105	char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
106	int op;
107
108	/* Invalid op */
109	op = -1;
110	prctl_invalid(_metadata, op, 0, 0, &sel, EINVAL);
111
112	/* PR_SYS_DISPATCH_OFF */
113	op = PR_SYS_DISPATCH_OFF;
114
115	/* offset != 0 */
116	prctl_invalid(_metadata, op, 0x1, 0x0, 0, EINVAL);
117
118	/* len != 0 */
119	prctl_invalid(_metadata, op, 0x0, 0xff, 0, EINVAL);
120
121	/* sel != NULL */
122	prctl_invalid(_metadata, op, 0x0, 0x0, &sel, EINVAL);
123
124	/* Valid parameter */
125	prctl_valid(_metadata, op, 0x0, 0x0, 0x0);
126
127	/* PR_SYS_DISPATCH_EXCLUSIVE_ON */
128	op = PR_SYS_DISPATCH_EXCLUSIVE_ON;
129
130	/* Dispatcher region is bad (offset > 0 && len == 0) */
131	prctl_invalid(_metadata, op, 0x1, 0x0, &sel, EINVAL);
132	prctl_invalid(_metadata, op, -1L, 0x0, &sel, EINVAL);
133
134	/* Invalid selector */
135	prctl_invalid(_metadata, op, 0x0, 0x1, (void *) -1, EFAULT);
136
137	/*
138	 * Dispatcher range overflows unsigned long
139	 */
140	prctl_invalid(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, 1, -1L, &sel, EINVAL);
141
142	/*
143	 * Allowed range overflows usigned long
144	 */
145	prctl_invalid(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, -1L, 0x1, &sel, EINVAL);
146
147	/* 0 len should fail for PR_SYS_DISPATCH_INCLUSIVE_ON */
148	prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 1, 0, 0, EINVAL);
149
150	/* Range wrap-around should fail */
151	prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, -1L, 2, 0, EINVAL);
152
153	/* Normal range shouldn't fail */
154	prctl_valid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, 0);
155
156	/* Invalid selector */
157	prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, (void *) -1, EFAULT);
158}
159
160/*
161 * Use global selector for handle_sigsys tests, to avoid passing
162 * selector to signal handler
163 */
164char glob_sel;
165int nr_syscalls_emulated;
166int si_code;
167int si_errno;
168unsigned long syscall_addr;
169
170static void handle_sigsys(int sig, siginfo_t *info, void *ucontext)
171{
172	si_code = info->si_code;
173	si_errno = info->si_errno;
174	syscall_addr = (unsigned long)info->si_call_addr;
175
176	if (info->si_syscall == MAGIC_SYSCALL_1)
177		nr_syscalls_emulated++;
178
179	/* In preparation for sigreturn. */
180	SYSCALL_DISPATCH_OFF(glob_sel);
181
182	/*
183	 * The tests for argument handling assume that `syscall(x) == x`. This
184	 * is a NOP on x86 because the syscall number is passed in %rax, which
185	 * happens to also be the function ABI return register.  Other
186	 * architectures may need to swizzle the arguments around.
187	 */
188#if defined(__riscv)
189/* REG_A7 is not defined in libc headers */
190# define REG_A7 (REG_A0 + 7)
191
192	((ucontext_t *)ucontext)->uc_mcontext.__gregs[REG_A0] =
193			((ucontext_t *)ucontext)->uc_mcontext.__gregs[REG_A7];
194#endif
195}
196
197int setup_sigsys_handler(void)
198{
199	struct sigaction act;
200	sigset_t mask;
201
202	memset(&act, 0, sizeof(act));
203	sigemptyset(&mask);
204	act.sa_sigaction = handle_sigsys;
205	act.sa_flags = SA_SIGINFO;
206	act.sa_mask = mask;
207	return sigaction(SIGSYS, &act, NULL);
208}
209
210TEST(dispatch_and_return)
211{
212	long ret;
213
214	glob_sel = 0;
215	nr_syscalls_emulated = 0;
216	si_code = 0;
217	si_errno = 0;
218
219	ASSERT_EQ(0, setup_sigsys_handler());
220
221	/* Make sure selector is good prior to prctl. */
222	SYSCALL_DISPATCH_OFF(glob_sel);
223
224	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &glob_sel);
225	ASSERT_EQ(0, ret) {
226		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
227	}
228
229	/* MAGIC_SYSCALL_1 doesn't exist. */
230	SYSCALL_DISPATCH_OFF(glob_sel);
231	ret = syscall(MAGIC_SYSCALL_1);
232	EXPECT_EQ(-1, ret) {
233		TH_LOG("Dispatch triggered unexpectedly");
234	}
235
236	/* MAGIC_SYSCALL_1 should be emulated. */
237	nr_syscalls_emulated = 0;
238	SYSCALL_DISPATCH_ON(glob_sel);
239
240	ret = syscall(MAGIC_SYSCALL_1);
241	EXPECT_EQ(MAGIC_SYSCALL_1, ret) {
242		TH_LOG("Failed to intercept syscall");
243	}
244	EXPECT_EQ(1, nr_syscalls_emulated) {
245		TH_LOG("Failed to emulate syscall");
246	}
247	ASSERT_EQ(SYS_USER_DISPATCH, si_code) {
248		TH_LOG("Bad si_code in SIGSYS");
249	}
250	ASSERT_EQ(0, si_errno) {
251		TH_LOG("Bad si_errno in SIGSYS");
252	}
253}
254
255TEST_SIGNAL(bad_selector, SIGSYS)
256{
257	long ret;
258	struct sigaction act;
259	sigset_t mask;
260	struct sysinfo info;
261
262	glob_sel = SYSCALL_DISPATCH_FILTER_ALLOW;
263	nr_syscalls_emulated = 0;
264	si_code = 0;
265	si_errno = 0;
266
267	memset(&act, 0, sizeof(act));
268	sigemptyset(&mask);
269
270	act.sa_sigaction = handle_sigsys;
271	act.sa_flags = SA_SIGINFO;
272	act.sa_mask = mask;
273
274	ret = sigaction(SIGSYS, &act, NULL);
275	ASSERT_EQ(0, ret);
276
277	/* Make sure selector is good prior to prctl. */
278	SYSCALL_DISPATCH_OFF(glob_sel);
279
280	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &glob_sel);
281	ASSERT_EQ(0, ret) {
282		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
283	}
284
285	glob_sel = -1;
286
287	sysinfo(&info);
288
289	/* Even though it is ready to catch SIGSYS, the signal is
290	 * supposed to be uncatchable.
291	 */
292
293	EXPECT_FALSE(true) {
294		TH_LOG("Unreachable!");
295	}
296}
297
298TEST(disable_dispatch)
299{
300	int ret;
301	struct sysinfo info;
302	char sel = 0;
303
304	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, &sel);
305	ASSERT_EQ(0, ret) {
306		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
307	}
308
309	/* MAGIC_SYSCALL_1 doesn't exist. */
310	SYSCALL_DISPATCH_OFF(glob_sel);
311
312	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0);
313	EXPECT_EQ(0, ret) {
314		TH_LOG("Failed to unset syscall user dispatch");
315	}
316
317	/* Shouldn't have any effect... */
318	SYSCALL_DISPATCH_ON(glob_sel);
319
320	ret = syscall(__NR_sysinfo, &info);
321	EXPECT_EQ(0, ret) {
322		TH_LOG("Dispatch triggered unexpectedly");
323	}
324}
325
326TEST(direct_dispatch_range)
327{
328	int ret = 0;
329	struct sysinfo info;
330	char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
331
332	/*
333	 * Instead of calculating libc addresses; allow the entire
334	 * memory map and lock the selector.
335	 */
336	ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, -1L, &sel);
337	ASSERT_EQ(0, ret) {
338		TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
339	}
340
341	SYSCALL_DISPATCH_ON(sel);
342
343	ret = sysinfo(&info);
344	ASSERT_EQ(0, ret) {
345		TH_LOG("Dispatch triggered unexpectedly");
346	}
347}
348
349static void test_range(struct __test_metadata *_metadata,
350		       unsigned long op, unsigned long off,
351		       unsigned long size, bool dispatch)
352{
353	nr_syscalls_emulated = 0;
354	SYSCALL_DISPATCH_OFF(glob_sel);
355	EXPECT_EQ(0, prctl(PR_SET_SYSCALL_USER_DISPATCH, op, off, size, &glob_sel));
356	SYSCALL_DISPATCH_ON(glob_sel);
357	if (dispatch) {
358		EXPECT_EQ(syscall(MAGIC_SYSCALL_1), MAGIC_SYSCALL_1);
359		EXPECT_EQ(nr_syscalls_emulated, 1);
360	} else {
361		EXPECT_EQ(syscall(MAGIC_SYSCALL_1), -1);
362		EXPECT_EQ(nr_syscalls_emulated, 0);
363	}
364}
365
366TEST(dispatch_range)
367{
368	ASSERT_EQ(0, setup_sigsys_handler());
369	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, 0, 0, true);
370	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr, 1, false);
371	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr-100, 200, false);
372	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr+1, 100, true);
373	test_range(_metadata, PR_SYS_DISPATCH_EXCLUSIVE_ON, syscall_addr-100, 100, true);
374	test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr, 1, true);
375	test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr-1, 1, false);
376	test_range(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, syscall_addr+1, 1, false);
377	SYSCALL_DISPATCH_OFF(glob_sel);
378}
379
380TEST_HARNESS_MAIN
~/ Read-only file preview.