get_syscall_info.c (6555B)
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