root/tools/testing/selftests/ptrace/get_syscall_info.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. kill_tracee
  2. sys_ptrace
  3. TEST

   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 
  16 static int
  17 kill_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 
  30 static long
  31 sys_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 
  43 TEST(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 
 271 TEST_HARNESS_MAIN

/* [<][>][^][v][top][bottom][index][help] */