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 311