272 lines · c
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 kernel7 * 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 int17kill_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 long31sys_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 0xbad5fed555 },56 {57 __NR_gettid,58 0xcaf0bea0,59 0xcaf1bea1,60 0xcaf2bea2,61 0xcaf3bea3,62 0xcaf4bea4,63 0xcaf5bea564 },65 {66 __NR_exit_group,67 0,68 0xfac1c0d1,69 0xfac2c0d2,70 0xfac3c0d3,71 0xfac4c0d4,72 0xfac5c0d573 }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_MAIN272