linux/tools/testing/selftests/ptrace/get_syscall_info.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
   4 * All rights reserved.
   5 *
   6 * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
   7 * matches userspace expectations.
   8 */
   9
  10#include "../kselftest_harness.h"
  11#include <err.h>
  12#include <signal.h>
  13#include <asm/unistd.h>
  14#include "linux/ptrace.h"
  15
  16static int
  17kill_tracee(pid_t pid)
  18{
  19        if (!pid)
  20                return 0;
  21
  22        int saved_errno = errno;
  23
  24        int rc = kill(pid, SIGKILL);
  25
  26        errno = saved_errno;
  27        return rc;
  28}
  29
  30static long
  31sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
  32{
  33        return syscall(__NR_ptrace, request, pid, addr, data);
  34}
  35
  36#define LOG_KILL_TRACEE(fmt, ...)                               \
  37        do {                                                    \
  38                kill_tracee(pid);                               \
  39                TH_LOG("wait #%d: " fmt,                        \
  40                       ptrace_stop, ##__VA_ARGS__);             \
  41        } while (0)
  42
  43TEST(get_syscall_info)
  44{
  45        static const unsigned long args[][7] = {
  46                /* a sequence of architecture-agnostic syscalls */
  47                {
  48                        __NR_chdir,
  49                        (unsigned long) "",
  50                        0xbad1fed1,
  51                        0xbad2fed2,
  52                        0xbad3fed3,
  53                        0xbad4fed4,
  54                        0xbad5fed5
  55                },
  56                {
  57                        __NR_gettid,
  58                        0xcaf0bea0,
  59                        0xcaf1bea1,
  60                        0xcaf2bea2,
  61                        0xcaf3bea3,
  62                        0xcaf4bea4,
  63                        0xcaf5bea5
  64                },
  65                {
  66                        __NR_exit_group,
  67                        0,
  68                        0xfac1c0d1,
  69                        0xfac2c0d2,
  70                        0xfac3c0d3,
  71                        0xfac4c0d4,
  72                        0xfac5c0d5
  73                }
  74        };
  75        const unsigned long *exp_args;
  76
  77        pid_t pid = fork();
  78
  79        ASSERT_LE(0, pid) {
  80                TH_LOG("fork: %m");
  81        }
  82
  83        if (pid == 0) {
  84                /* get the pid before PTRACE_TRACEME */
  85                pid = getpid();
  86                ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
  87                        TH_LOG("PTRACE_TRACEME: %m");
  88                }
  89                ASSERT_EQ(0, kill(pid, SIGSTOP)) {
  90                        /* cannot happen */
  91                        TH_LOG("kill SIGSTOP: %m");
  92                }
  93                for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
  94                        syscall(args[i][0],
  95                                args[i][1], args[i][2], args[i][3],
  96                                args[i][4], args[i][5], args[i][6]);
  97                }
  98                /* unreachable */
  99                _exit(1);
 100        }
 101
 102        const struct {
 103                unsigned int is_error;
 104                int rval;
 105        } *exp_param, exit_param[] = {
 106                { 1, -ENOENT }, /* chdir */
 107                { 0, pid }      /* gettid */
 108        };
 109
 110        unsigned int ptrace_stop;
 111
 112        for (ptrace_stop = 0; ; ++ptrace_stop) {
 113                struct ptrace_syscall_info info = {
 114                        .op = 0xff      /* invalid PTRACE_SYSCALL_INFO_* op */
 115                };
 116                const size_t size = sizeof(info);
 117                const int expected_none_size =
 118                        (void *) &info.entry - (void *) &info;
 119                const int expected_entry_size =
 120                        (void *) &info.entry.args[6] - (void *) &info;
 121                const int expected_exit_size =
 122                        (void *) (&info.exit.is_error + 1) -
 123                        (void *) &info;
 124                int status;
 125                long rc;
 126
 127                ASSERT_EQ(pid, wait(&status)) {
 128                        /* cannot happen */
 129                        LOG_KILL_TRACEE("wait: %m");
 130                }
 131                if (WIFEXITED(status)) {
 132                        pid = 0;        /* the tracee is no more */
 133                        ASSERT_EQ(0, WEXITSTATUS(status));
 134                        break;
 135                }
 136                ASSERT_FALSE(WIFSIGNALED(status)) {
 137                        pid = 0;        /* the tracee is no more */
 138                        LOG_KILL_TRACEE("unexpected signal %u",
 139                                        WTERMSIG(status));
 140                }
 141                ASSERT_TRUE(WIFSTOPPED(status)) {
 142                        /* cannot happen */
 143                        LOG_KILL_TRACEE("unexpected wait status %#x", status);
 144                }
 145
 146                switch (WSTOPSIG(status)) {
 147                case SIGSTOP:
 148                        ASSERT_EQ(0, ptrace_stop) {
 149                                LOG_KILL_TRACEE("unexpected signal stop");
 150                        }
 151                        ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
 152                                                PTRACE_O_TRACESYSGOOD)) {
 153                                LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
 154                        }
 155                        ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
 156                                                      pid, size,
 157                                                      (unsigned long) &info))) {
 158                                LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
 159                        }
 160                        ASSERT_EQ(expected_none_size, rc) {
 161                                LOG_KILL_TRACEE("signal stop mismatch");
 162                        }
 163                        ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
 164                                LOG_KILL_TRACEE("signal stop mismatch");
 165                        }
 166                        ASSERT_TRUE(info.arch) {
 167                                LOG_KILL_TRACEE("signal stop mismatch");
 168                        }
 169                        ASSERT_TRUE(info.instruction_pointer) {
 170                                LOG_KILL_TRACEE("signal stop mismatch");
 171                        }
 172                        ASSERT_TRUE(info.stack_pointer) {
 173                                LOG_KILL_TRACEE("signal stop mismatch");
 174                        }
 175                        break;
 176
 177                case SIGTRAP | 0x80:
 178                        ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
 179                                                      pid, size,
 180                                                      (unsigned long) &info))) {
 181                                LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
 182                        }
 183                        switch (ptrace_stop) {
 184                        case 1: /* entering chdir */
 185                        case 3: /* entering gettid */
 186                        case 5: /* entering exit_group */
 187                                exp_args = args[ptrace_stop / 2];
 188                                ASSERT_EQ(expected_entry_size, rc) {
 189                                        LOG_KILL_TRACEE("entry stop mismatch");
 190                                }
 191                                ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
 192                                        LOG_KILL_TRACEE("entry stop mismatch");
 193                                }
 194                                ASSERT_TRUE(info.arch) {
 195                                        LOG_KILL_TRACEE("entry stop mismatch");
 196                                }
 197                                ASSERT_TRUE(info.instruction_pointer) {
 198                                        LOG_KILL_TRACEE("entry stop mismatch");
 199                                }
 200                                ASSERT_TRUE(info.stack_pointer) {
 201                                        LOG_KILL_TRACEE("entry stop mismatch");
 202                                }
 203                                ASSERT_EQ(exp_args[0], info.entry.nr) {
 204                                        LOG_KILL_TRACEE("entry stop mismatch");
 205                                }
 206                                ASSERT_EQ(exp_args[1], info.entry.args[0]) {
 207                                        LOG_KILL_TRACEE("entry stop mismatch");
 208                                }
 209                                ASSERT_EQ(exp_args[2], info.entry.args[1]) {
 210                                        LOG_KILL_TRACEE("entry stop mismatch");
 211                                }
 212                                ASSERT_EQ(exp_args[3], info.entry.args[2]) {
 213                                        LOG_KILL_TRACEE("entry stop mismatch");
 214                                }
 215                                ASSERT_EQ(exp_args[4], info.entry.args[3]) {
 216                                        LOG_KILL_TRACEE("entry stop mismatch");
 217                                }
 218                                ASSERT_EQ(exp_args[5], info.entry.args[4]) {
 219                                        LOG_KILL_TRACEE("entry stop mismatch");
 220                                }
 221                                ASSERT_EQ(exp_args[6], info.entry.args[5]) {
 222                                        LOG_KILL_TRACEE("entry stop mismatch");
 223                                }
 224                                break;
 225                        case 2: /* exiting chdir */
 226                        case 4: /* exiting gettid */
 227                                exp_param = &exit_param[ptrace_stop / 2 - 1];
 228                                ASSERT_EQ(expected_exit_size, rc) {
 229                                        LOG_KILL_TRACEE("exit stop mismatch");
 230                                }
 231                                ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
 232                                        LOG_KILL_TRACEE("exit stop mismatch");
 233                                }
 234                                ASSERT_TRUE(info.arch) {
 235                                        LOG_KILL_TRACEE("exit stop mismatch");
 236                                }
 237                                ASSERT_TRUE(info.instruction_pointer) {
 238                                        LOG_KILL_TRACEE("exit stop mismatch");
 239                                }
 240                                ASSERT_TRUE(info.stack_pointer) {
 241                                        LOG_KILL_TRACEE("exit stop mismatch");
 242                                }
 243                                ASSERT_EQ(exp_param->is_error,
 244                                          info.exit.is_error) {
 245                                        LOG_KILL_TRACEE("exit stop mismatch");
 246                                }
 247                                ASSERT_EQ(exp_param->rval, info.exit.rval) {
 248                                        LOG_KILL_TRACEE("exit stop mismatch");
 249                                }
 250                                break;
 251                        default:
 252                                LOG_KILL_TRACEE("unexpected syscall stop");
 253                                abort();
 254                        }
 255                        break;
 256
 257                default:
 258                        LOG_KILL_TRACEE("unexpected stop signal %#x",
 259                                        WSTOPSIG(status));
 260                        abort();
 261                }
 262
 263                ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
 264                        LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
 265                }
 266        }
 267
 268        ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
 269}
 270
 271TEST_HARNESS_MAIN
 272