Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/ptrace: add a test case for PTRACE_GET_SYSCALL_INFO

Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the
kernel matches userspace expectations.

[akpm@linux-foundation.org: coding-style fixes]
Link: http://lkml.kernel.org/r/20190510152852.GG28558@altlinux.org
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
Acked-by: Shuah Khan <shuah@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Elvira Khabirova <lineprinter@altlinux.org>
Cc: Eugene Syromyatnikov <esyr@redhat.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Greentime Hu <greentime@andestech.com>
Cc: Helge Deller <deller@gmx.de> [parisc]
Cc: James E.J. Bottomley <jejb@parisc-linux.org>
Cc: James Hogan <jhogan@kernel.org>
Cc: kbuild test robot <lkp@intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Paul Burton <paul.burton@mips.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Richard Kuo <rkuo@codeaurora.org>
Cc: Vincent Chen <deanbo422@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Dmitry V. Levin and committed by
Linus Torvalds
ac76de55 201766a2

+273 -1
+1
tools/testing/selftests/ptrace/.gitignore
··· 1 + get_syscall_info 1 2 peeksiginfo
+1 -1
tools/testing/selftests/ptrace/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 CFLAGS += -iquote../../../../include/uapi -Wall 3 3 4 - TEST_GEN_PROGS := peeksiginfo 4 + TEST_GEN_PROGS := get_syscall_info peeksiginfo 5 5 6 6 include ../lib.mk
+271
tools/testing/selftests/ptrace/get_syscall_info.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 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