| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
- * All rights reserved.
- *
- * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
- * matches userspace expectations.
- */
- #include "kselftest_harness.h"
- #include <err.h>
- #include <signal.h>
- #include <asm/unistd.h>
- #include "linux/ptrace.h"
- static int
- kill_tracee(pid_t pid)
- {
- if (!pid)
- return 0;
- int saved_errno = errno;
- int rc = kill(pid, SIGKILL);
- errno = saved_errno;
- return rc;
- }
- static long
- sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
- {
- return syscall(__NR_ptrace, request, pid, addr, data);
- }
- #define LOG_KILL_TRACEE(fmt, ...) \
- do { \
- kill_tracee(pid); \
- TH_LOG("wait #%d: " fmt, \
- ptrace_stop, ##__VA_ARGS__); \
- } while (0)
- TEST(get_syscall_info)
- {
- static const unsigned long args[][7] = {
- /* a sequence of architecture-agnostic syscalls */
- {
- __NR_chdir,
- (unsigned long) "",
- 0xbad1fed1,
- 0xbad2fed2,
- 0xbad3fed3,
- 0xbad4fed4,
- 0xbad5fed5
- },
- {
- __NR_gettid,
- 0xcaf0bea0,
- 0xcaf1bea1,
- 0xcaf2bea2,
- 0xcaf3bea3,
- 0xcaf4bea4,
- 0xcaf5bea5
- },
- {
- __NR_exit_group,
- 0,
- 0xfac1c0d1,
- 0xfac2c0d2,
- 0xfac3c0d3,
- 0xfac4c0d4,
- 0xfac5c0d5
- }
- };
- const unsigned long *exp_args;
- pid_t pid = fork();
- ASSERT_LE(0, pid) {
- TH_LOG("fork: %m");
- }
- if (pid == 0) {
- /* get the pid before PTRACE_TRACEME */
- pid = getpid();
- ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
- TH_LOG("PTRACE_TRACEME: %m");
- }
- ASSERT_EQ(0, kill(pid, SIGSTOP)) {
- /* cannot happen */
- TH_LOG("kill SIGSTOP: %m");
- }
- for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
- syscall(args[i][0],
- args[i][1], args[i][2], args[i][3],
- args[i][4], args[i][5], args[i][6]);
- }
- /* unreachable */
- _exit(1);
- }
- const struct {
- unsigned int is_error;
- int rval;
- } *exp_param, exit_param[] = {
- { 1, -ENOENT }, /* chdir */
- { 0, pid } /* gettid */
- };
- unsigned int ptrace_stop;
- for (ptrace_stop = 0; ; ++ptrace_stop) {
- struct ptrace_syscall_info info = {
- .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
- };
- const size_t size = sizeof(info);
- const int expected_none_size =
- (void *) &info.entry - (void *) &info;
- const int expected_entry_size =
- (void *) &info.entry.args[6] - (void *) &info;
- const int expected_exit_size =
- (void *) (&info.exit.is_error + 1) -
- (void *) &info;
- int status;
- long rc;
- ASSERT_EQ(pid, wait(&status)) {
- /* cannot happen */
- LOG_KILL_TRACEE("wait: %m");
- }
- if (WIFEXITED(status)) {
- pid = 0; /* the tracee is no more */
- ASSERT_EQ(0, WEXITSTATUS(status));
- break;
- }
- ASSERT_FALSE(WIFSIGNALED(status)) {
- pid = 0; /* the tracee is no more */
- LOG_KILL_TRACEE("unexpected signal %u",
- WTERMSIG(status));
- }
- ASSERT_TRUE(WIFSTOPPED(status)) {
- /* cannot happen */
- LOG_KILL_TRACEE("unexpected wait status %#x", status);
- }
- switch (WSTOPSIG(status)) {
- case SIGSTOP:
- ASSERT_EQ(0, ptrace_stop) {
- LOG_KILL_TRACEE("unexpected signal stop");
- }
- ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
- PTRACE_O_TRACESYSGOOD)) {
- LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
- }
- ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
- pid, size,
- (unsigned long) &info))) {
- LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
- }
- ASSERT_EQ(expected_none_size, rc) {
- LOG_KILL_TRACEE("signal stop mismatch");
- }
- ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
- LOG_KILL_TRACEE("signal stop mismatch");
- }
- ASSERT_TRUE(info.arch) {
- LOG_KILL_TRACEE("signal stop mismatch");
- }
- ASSERT_TRUE(info.instruction_pointer) {
- LOG_KILL_TRACEE("signal stop mismatch");
- }
- ASSERT_TRUE(info.stack_pointer) {
- LOG_KILL_TRACEE("signal stop mismatch");
- }
- break;
- case SIGTRAP | 0x80:
- ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
- pid, size,
- (unsigned long) &info))) {
- LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
- }
- switch (ptrace_stop) {
- case 1: /* entering chdir */
- case 3: /* entering gettid */
- case 5: /* entering exit_group */
- exp_args = args[ptrace_stop / 2];
- ASSERT_EQ(expected_entry_size, rc) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_TRUE(info.arch) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_TRUE(info.instruction_pointer) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_TRUE(info.stack_pointer) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[0], info.entry.nr) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[1], info.entry.args[0]) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[2], info.entry.args[1]) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[3], info.entry.args[2]) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[4], info.entry.args[3]) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[5], info.entry.args[4]) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- ASSERT_EQ(exp_args[6], info.entry.args[5]) {
- LOG_KILL_TRACEE("entry stop mismatch");
- }
- break;
- case 2: /* exiting chdir */
- case 4: /* exiting gettid */
- exp_param = &exit_param[ptrace_stop / 2 - 1];
- ASSERT_EQ(expected_exit_size, rc) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- ASSERT_TRUE(info.arch) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- ASSERT_TRUE(info.instruction_pointer) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- ASSERT_TRUE(info.stack_pointer) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- ASSERT_EQ(exp_param->is_error,
- info.exit.is_error) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- ASSERT_EQ(exp_param->rval, info.exit.rval) {
- LOG_KILL_TRACEE("exit stop mismatch");
- }
- break;
- default:
- LOG_KILL_TRACEE("unexpected syscall stop");
- abort();
- }
- break;
- default:
- LOG_KILL_TRACEE("unexpected stop signal %#x",
- WSTOPSIG(status));
- abort();
- }
- ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
- LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
- }
- }
- ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
- }
- TEST_HARNESS_MAIN
|