brintos

brintos / linux-shallow public Read only

0
0
Text · 7.5 KiB · d975a67 Raw
327 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2020 Collabora Ltd.4 *5 * Test code for syscall user dispatch6 */7 8#define _GNU_SOURCE9#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_DISPATCH18# define PR_SET_SYSCALL_USER_DISPATCH	5919# define PR_SYS_DISPATCH_OFF	020# define PR_SYS_DISPATCH_ON	121# define SYSCALL_DISPATCH_FILTER_ALLOW	022# define SYSCALL_DISPATCH_FILTER_BLOCK	123#endif24 25#ifndef SYS_USER_DISPATCH26# define SYS_USER_DISPATCH	227#endif28 29#ifdef __NR_syscalls30# define MAGIC_SYSCALL_1 (__NR_syscalls + 1) /* Bad Linux syscall number */31#else32# define MAGIC_SYSCALL_1 (0xff00)  /* Bad Linux syscall number */33#endif34 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 is41 *   able to trigger SIGSYS on a syscall.42 *43 * - bad_selector: Test that a bad selector value triggers SIGSYS with44 *   si_errno EINVAL.45 *46 * - bad_prctl_param: Test that the API correctly rejects invalid47 *   parameters on prctl48 *49 * - dispatch_and_return: Test that a syscall is selectively dispatched50 *   to userspace depending on the value of selector.51 *52 * - disable_dispatch: Test that the PR_SYS_DISPATCH_OFF correctly53 *   disables the dispatcher54 *55 * - direct_dispatch_range: Test that a syscall within the allowed range56 *   can bypass the dispatcher.57 */58 59TEST_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 82TEST(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 long127	 */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 long135	 */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 passing144 * selector to signal handler145 */146char glob_sel;147int nr_syscalls_emulated;148int si_code;149int si_errno;150 151static 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`. This164	 * is a NOP on x86 because the syscall number is passed in %rax, which165	 * happens to also be the function ABI return register.  Other166	 * 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#endif175}176 177TEST(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 232TEST_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 is267	 * supposed to be uncatchable.268	 */269 270	EXPECT_FALSE(true) {271		TH_LOG("Unreachable!");272	}273}274 275TEST(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 303TEST(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 entire311	 * 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 326TEST_HARNESS_MAIN327