linux/tools/testing/selftests/syscall_user_dispatch/sud_test.c
<<
>>
Prefs
   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
  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 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 */
 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
 163TEST(dispatch_and_return)
 164{
 165        long ret;
 166        struct sigaction act;
 167        sigset_t mask;
 168
 169        glob_sel = 0;
 170        nr_syscalls_emulated = 0;
 171        si_code = 0;
 172        si_errno = 0;
 173
 174        memset(&act, 0, sizeof(act));
 175        sigemptyset(&mask);
 176
 177        act.sa_sigaction = handle_sigsys;
 178        act.sa_flags = SA_SIGINFO;
 179        act.sa_mask = mask;
 180
 181        ret = sigaction(SIGSYS, &act, NULL);
 182        ASSERT_EQ(0, ret);
 183
 184        /* Make sure selector is good prior to prctl. */
 185        SYSCALL_DISPATCH_OFF(glob_sel);
 186
 187        ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel);
 188        ASSERT_EQ(0, ret) {
 189                TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
 190        }
 191
 192        /* MAGIC_SYSCALL_1 doesn't exist. */
 193        SYSCALL_DISPATCH_OFF(glob_sel);
 194        ret = syscall(MAGIC_SYSCALL_1);
 195        EXPECT_EQ(-1, ret) {
 196                TH_LOG("Dispatch triggered unexpectedly");
 197        }
 198
 199        /* MAGIC_SYSCALL_1 should be emulated. */
 200        nr_syscalls_emulated = 0;
 201        SYSCALL_DISPATCH_ON(glob_sel);
 202
 203        ret = syscall(MAGIC_SYSCALL_1);
 204        EXPECT_EQ(MAGIC_SYSCALL_1, ret) {
 205                TH_LOG("Failed to intercept syscall");
 206        }
 207        EXPECT_EQ(1, nr_syscalls_emulated) {
 208                TH_LOG("Failed to emulate syscall");
 209        }
 210        ASSERT_EQ(SYS_USER_DISPATCH, si_code) {
 211                TH_LOG("Bad si_code in SIGSYS");
 212        }
 213        ASSERT_EQ(0, si_errno) {
 214                TH_LOG("Bad si_errno in SIGSYS");
 215        }
 216}
 217
 218TEST_SIGNAL(bad_selector, SIGSYS)
 219{
 220        long ret;
 221        struct sigaction act;
 222        sigset_t mask;
 223        struct sysinfo info;
 224
 225        glob_sel = SYSCALL_DISPATCH_FILTER_ALLOW;
 226        nr_syscalls_emulated = 0;
 227        si_code = 0;
 228        si_errno = 0;
 229
 230        memset(&act, 0, sizeof(act));
 231        sigemptyset(&mask);
 232
 233        act.sa_sigaction = handle_sigsys;
 234        act.sa_flags = SA_SIGINFO;
 235        act.sa_mask = mask;
 236
 237        ret = sigaction(SIGSYS, &act, NULL);
 238        ASSERT_EQ(0, ret);
 239
 240        /* Make sure selector is good prior to prctl. */
 241        SYSCALL_DISPATCH_OFF(glob_sel);
 242
 243        ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &glob_sel);
 244        ASSERT_EQ(0, ret) {
 245                TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
 246        }
 247
 248        glob_sel = -1;
 249
 250        sysinfo(&info);
 251
 252        /* Even though it is ready to catch SIGSYS, the signal is
 253         * supposed to be uncatchable.
 254         */
 255
 256        EXPECT_FALSE(true) {
 257                TH_LOG("Unreachable!");
 258        }
 259}
 260
 261TEST(disable_dispatch)
 262{
 263        int ret;
 264        struct sysinfo info;
 265        char sel = 0;
 266
 267        ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, 0, &sel);
 268        ASSERT_EQ(0, ret) {
 269                TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
 270        }
 271
 272        /* MAGIC_SYSCALL_1 doesn't exist. */
 273        SYSCALL_DISPATCH_OFF(glob_sel);
 274
 275        ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF, 0, 0, 0);
 276        EXPECT_EQ(0, ret) {
 277                TH_LOG("Failed to unset syscall user dispatch");
 278        }
 279
 280        /* Shouldn't have any effect... */
 281        SYSCALL_DISPATCH_ON(glob_sel);
 282
 283        ret = syscall(__NR_sysinfo, &info);
 284        EXPECT_EQ(0, ret) {
 285                TH_LOG("Dispatch triggered unexpectedly");
 286        }
 287}
 288
 289TEST(direct_dispatch_range)
 290{
 291        int ret = 0;
 292        struct sysinfo info;
 293        char sel = SYSCALL_DISPATCH_FILTER_ALLOW;
 294
 295        /*
 296         * Instead of calculating libc addresses; allow the entire
 297         * memory map and lock the selector.
 298         */
 299        ret = prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, 0, -1L, &sel);
 300        ASSERT_EQ(0, ret) {
 301                TH_LOG("Kernel does not support CONFIG_SYSCALL_USER_DISPATCH");
 302        }
 303
 304        SYSCALL_DISPATCH_ON(sel);
 305
 306        ret = sysinfo(&info);
 307        ASSERT_EQ(0, ret) {
 308                TH_LOG("Dispatch triggered unexpectedly");
 309        }
 310}
 311
 312TEST_HARNESS_MAIN
 313