| 1 | |
| 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)
|
| 37 | #else
|
| 38 | # define MAGIC_SYSCALL_1 (0xff00)
|
| 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 | |
| 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 |
|
| 65 | TEST_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 |
|
| 88 | static 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 |
|
| 95 | static 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 |
|
| 103 | TEST(bad_prctl_param)
|
| 104 | {
|
| 105 | char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
|
| 106 | int op;
|
| 107 |
|
| 108 |
|
| 109 | op = -1;
|
| 110 | prctl_invalid(_metadata, op, 0, 0, &sel, EINVAL);
|
| 111 |
|
| 112 |
|
| 113 | op = PR_SYS_DISPATCH_OFF;
|
| 114 |
|
| 115 |
|
| 116 | prctl_invalid(_metadata, op, 0x1, 0x0, 0, EINVAL);
|
| 117 |
|
| 118 |
|
| 119 | prctl_invalid(_metadata, op, 0x0, 0xff, 0, EINVAL);
|
| 120 |
|
| 121 |
|
| 122 | prctl_invalid(_metadata, op, 0x0, 0x0, &sel, EINVAL);
|
| 123 |
|
| 124 |
|
| 125 | prctl_valid(_metadata, op, 0x0, 0x0, 0x0);
|
| 126 |
|
| 127 |
|
| 128 | op = PR_SYS_DISPATCH_EXCLUSIVE_ON;
|
| 129 |
|
| 130 |
|
| 131 | prctl_invalid(_metadata, op, 0x1, 0x0, &sel, EINVAL);
|
| 132 | prctl_invalid(_metadata, op, -1L, 0x0, &sel, EINVAL);
|
| 133 |
|
| 134 |
|
| 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 |
|
| 148 | prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 1, 0, 0, EINVAL);
|
| 149 |
|
| 150 |
|
| 151 | prctl_invalid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, -1L, 2, 0, EINVAL);
|
| 152 |
|
| 153 |
|
| 154 | prctl_valid(_metadata, PR_SYS_DISPATCH_INCLUSIVE_ON, 2, 3, 0);
|
| 155 |
|
| 156 |
|
| 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 |
|
| 164 | char glob_sel;
|
| 165 | int nr_syscalls_emulated;
|
| 166 | int si_code;
|
| 167 | int si_errno;
|
| 168 | unsigned long syscall_addr;
|
| 169 |
|
| 170 | static 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 |
|
| 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 |
|
| 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 |
|
| 197 | int 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 |
|
| 210 | TEST(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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 255 | TEST_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 |
|
| 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 | |
| 290 | * supposed to be uncatchable.
|
| 291 |
|
| 292 |
|
| 293 | EXPECT_FALSE(true) {
|
| 294 | TH_LOG("Unreachable!");
|
| 295 | }
|
| 296 | }
|
| 297 |
|
| 298 | TEST(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 |
|
| 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 |
|
| 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 |
|
| 326 | TEST(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 |
|
| 349 | static 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 |
|
| 366 | TEST(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 |
|
| 380 | TEST_HARNESS_MAIN
|