Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/tools/testing/selftests/ptrace/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 11 kB image not shown  

Quelle  set_syscall_info.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2018-2025 Dmitry V. Levin <ldv@strace.io>
 * All rights reserved.
 *
 * Check whether PTRACE_SET_SYSCALL_INFO semantics implemented in the kernel
 * matches userspace expectations.
 */


#include "../kselftest_harness.h"
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <asm/unistd.h>
#include <linux/types.h>
#include <linux/ptrace.h>

#if defined(_MIPS_SIM) && _MIPS_SIM == _MIPS_SIM_NABI32
/*
 * MIPS N32 is the only architecture where __kernel_ulong_t
 * does not match the bitness of syscall arguments.
 */

typedef unsigned long long kernel_ulong_t;
#else
typedef __kernel_ulong_t kernel_ulong_t;
#endif

struct si_entry {
 int nr;
 kernel_ulong_t args[6];
};
struct si_exit {
 unsigned int is_error;
 int rval;
};

static unsigned int ptrace_stop;
static pid_t tracee_pid;

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(tracee_pid);   \
  TH_LOG("wait #%d: " fmt,   \
         ptrace_stop, ##__VA_ARGS__);  \
 } while (0)

static void
check_psi_entry(struct __test_metadata *_metadata,
  const struct ptrace_syscall_info *info,
  const struct si_entry *exp_entry,
  const char *text)
{
 unsigned int i;
 int exp_nr = exp_entry->nr;
#if defined __s390__ || defined __s390x__
 /* s390 is the only architecture that has 16-bit syscall numbers */
 exp_nr &= 0xffff;
#endif

 ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info->op) {
  LOG_KILL_TRACEE("%s: entry stop mismatch", text);
 }
 ASSERT_TRUE(info->arch) {
  LOG_KILL_TRACEE("%s: entry stop mismatch", text);
 }
 ASSERT_TRUE(info->instruction_pointer) {
  LOG_KILL_TRACEE("%s: entry stop mismatch", text);
 }
 ASSERT_TRUE(info->stack_pointer) {
  LOG_KILL_TRACEE("%s: entry stop mismatch", text);
 }
 ASSERT_EQ(exp_nr, info->entry.nr) {
  LOG_KILL_TRACEE("%s: syscall nr mismatch", text);
 }
 for (i = 0; i < ARRAY_SIZE(exp_entry->args); ++i) {
  ASSERT_EQ(exp_entry->args[i], info->entry.args[i]) {
   LOG_KILL_TRACEE("%s: syscall arg #%u mismatch",
     text, i);
  }
 }
}

static void
check_psi_exit(struct __test_metadata *_metadata,
        const struct ptrace_syscall_info *info,
        const struct si_exit *exp_exit,
        const char *text)
{
 ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info->op) {
  LOG_KILL_TRACEE("%s: exit stop mismatch", text);
 }
 ASSERT_TRUE(info->arch) {
  LOG_KILL_TRACEE("%s: exit stop mismatch", text);
 }
 ASSERT_TRUE(info->instruction_pointer) {
  LOG_KILL_TRACEE("%s: exit stop mismatch", text);
 }
 ASSERT_TRUE(info->stack_pointer) {
  LOG_KILL_TRACEE("%s: exit stop mismatch", text);
 }
 ASSERT_EQ(exp_exit->is_error, info->exit.is_error) {
  LOG_KILL_TRACEE("%s: exit stop mismatch", text);
 }
 ASSERT_EQ(exp_exit->rval, info->exit.rval) {
  LOG_KILL_TRACEE("%s: exit stop mismatch", text);
 }
}

TEST(set_syscall_info)
{
 const pid_t tracer_pid = getpid();
 const kernel_ulong_t dummy[] = {
  (kernel_ulong_t) 0xdad0bef0bad0fed0ULL,
  (kernel_ulong_t) 0xdad1bef1bad1fed1ULL,
  (kernel_ulong_t) 0xdad2bef2bad2fed2ULL,
  (kernel_ulong_t) 0xdad3bef3bad3fed3ULL,
  (kernel_ulong_t) 0xdad4bef4bad4fed4ULL,
  (kernel_ulong_t) 0xdad5bef5bad5fed5ULL,
 };
 int splice_in[2], splice_out[2];

 ASSERT_EQ(0, pipe(splice_in));
 ASSERT_EQ(0, pipe(splice_out));
 ASSERT_EQ(sizeof(dummy), write(splice_in[1], dummy, sizeof(dummy)));

 const struct {
  struct si_entry entry[2];
  struct si_exit exit[2];
 } si[] = {
  /* change scno, keep non-error rval */
  {
   {
    {
     __NR_gettid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     __NR_getppid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 0, tracer_pid }, { 0, tracer_pid }
   }
  },

  /* set scno to -1, keep error rval */
  {
   {
    {
     __NR_chdir,
     {
      (uintptr_t) ".",
      dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     -1,
     {
      (uintptr_t) ".",
      dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 1, -ENOSYS }, { 1, -ENOSYS }
   }
  },

  /* keep scno, change non-error rval */
  {
   {
    {
     __NR_getppid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     __NR_getppid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 0, tracer_pid }, { 0, tracer_pid + 1 }
   }
  },

  /* change arg1, keep non-error rval */
  {
   {
    {
     __NR_chdir,
     {
      (uintptr_t) "",
      dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     __NR_chdir,
     {
      (uintptr_t) ".",
      dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 0, 0 }, { 0, 0 }
   }
  },

  /* set scno to -1, change error rval to non-error */
  {
   {
    {
     __NR_gettid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     -1,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 1, -ENOSYS }, { 0, tracer_pid }
   }
  },

  /* change scno, change non-error rval to error */
  {
   {
    {
     __NR_chdir,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     __NR_getppid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 0, tracer_pid }, { 1, -EISDIR }
   }
  },

  /* change scno and all args, change non-error rval */
  {
   {
    {
     __NR_gettid,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     __NR_splice,
     {
      splice_in[0], 0, splice_out[1], 0,
      sizeof(dummy), SPLICE_F_NONBLOCK
     }
    }
   }, {
    { 0, sizeof(dummy) }, { 0, sizeof(dummy) + 1 }
   }
  },

  /* change arg1, no exit stop */
  {
   {
    {
     __NR_exit_group,
     {
      dummy[0], dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }, {
     __NR_exit_group,
     {
      0, dummy[1], dummy[2],
      dummy[3], dummy[4], dummy[5]
     }
    }
   }, {
    { 0, 0 }, { 0, 0 }
   }
  },
 };

 long rc;
 unsigned int i;

 tracee_pid = fork();

 ASSERT_LE(0, tracee_pid) {
  TH_LOG("fork: %m");
 }

 if (tracee_pid == 0) {
  /* get the pid before PTRACE_TRACEME */
  tracee_pid = getpid();
  ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
   TH_LOG("PTRACE_TRACEME: %m");
  }
  ASSERT_EQ(0, kill(tracee_pid, SIGSTOP)) {
   /* cannot happen */
   TH_LOG("kill SIGSTOP: %m");
  }
  for (i = 0; i < ARRAY_SIZE(si); ++i) {
   rc = syscall(si[i].entry[0].nr,
         si[i].entry[0].args[0],
         si[i].entry[0].args[1],
         si[i].entry[0].args[2],
         si[i].entry[0].args[3],
         si[i].entry[0].args[4],
         si[i].entry[0].args[5]);
   if (si[i].exit[1].is_error) {
    if (rc != -1 || errno != -si[i].exit[1].rval)
     break;
   } else {
    if (rc != si[i].exit[1].rval)
     break;
   }
  }
  /*
 * Something went wrong, but in this state tracee
 * cannot reliably issue syscalls, so just crash.
 */

  *(volatile unsigned char *) (uintptr_t) i = 42;
  /* unreachable */
  _exit(i + 1);
 }

 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_entry_size =
   (void *) &info.entry.args[6] - (void *) &info;
  const int expected_exit_size =
   (void *) (&info.exit.is_error + 1) -
   (void *) &info;
  int status;

  ASSERT_EQ(tracee_pid, wait(&status)) {
   /* cannot happen */
   LOG_KILL_TRACEE("wait: %m");
  }
  if (WIFEXITED(status)) {
   tracee_pid = 0; /* the tracee is no more */
   ASSERT_EQ(0, WEXITSTATUS(status)) {
    LOG_KILL_TRACEE("unexpected exit status %u",
      WEXITSTATUS(status));
   }
   break;
  }
  ASSERT_FALSE(WIFSIGNALED(status)) {
   tracee_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);
  }

  ASSERT_LT(ptrace_stop, ARRAY_SIZE(si) * 2) {
   LOG_KILL_TRACEE("ptrace stop overflow");
  }

  switch (WSTOPSIG(status)) {
  case SIGSTOP:
   ASSERT_EQ(0, ptrace_stop) {
    LOG_KILL_TRACEE("unexpected signal stop");
   }
   ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, tracee_pid,
      0, PTRACE_O_TRACESYSGOOD)) {
    LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
   }
   break;

  case SIGTRAP | 0x80:
   ASSERT_LT(0, ptrace_stop) {
    LOG_KILL_TRACEE("unexpected syscall stop");
   }
   ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
            tracee_pid, size,
            (uintptr_t) &info))) {
    LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1: %m");
   }
   if (ptrace_stop & 1) {
    /* entering syscall */
    const struct si_entry *exp_entry =
     &si[ptrace_stop / 2].entry[0];
    const struct si_entry *set_entry =
     &si[ptrace_stop / 2].entry[1];

    /* check ptrace_syscall_info before the changes */
    ASSERT_EQ(expected_entry_size, rc) {
     LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1"
       ": entry stop mismatch");
    }
    check_psi_entry(_metadata, &info, exp_entry,
      "PTRACE_GET_SYSCALL_INFO #1");

    /* apply the changes */
    info.entry.nr = set_entry->nr;
    for (i = 0; i < ARRAY_SIZE(set_entry->args); ++i)
     info.entry.args[i] = set_entry->args[i];
    ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
       tracee_pid, size,
       (uintptr_t) &info)) {
     LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
    }

    /* check ptrace_syscall_info after the changes */
    memset(&info, 0, sizeof(info));
    info.op = 0xff;
    ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
             tracee_pid, size,
             (uintptr_t) &info))) {
     LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
    }
    ASSERT_EQ(expected_entry_size, rc) {
     LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2"
       ": entry stop mismatch");
    }
    check_psi_entry(_metadata, &info, set_entry,
      "PTRACE_GET_SYSCALL_INFO #2");
   } else {
    /* exiting syscall */
    const struct si_exit *exp_exit =
     &si[ptrace_stop / 2 - 1].exit[0];
    const struct si_exit *set_exit =
     &si[ptrace_stop / 2 - 1].exit[1];

    /* check ptrace_syscall_info before the changes */
    ASSERT_EQ(expected_exit_size, rc) {
     LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1"
       ": exit stop mismatch");
    }
    check_psi_exit(_metadata, &info, exp_exit,
      "PTRACE_GET_SYSCALL_INFO #1");

    /* apply the changes */
    info.exit.is_error = set_exit->is_error;
    info.exit.rval = set_exit->rval;
    ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
       tracee_pid, size,
       (uintptr_t) &info)) {
     LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
    }

    /* check ptrace_syscall_info after the changes */
    memset(&info, 0, sizeof(info));
    info.op = 0xff;
    ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
             tracee_pid, size,
             (uintptr_t) &info))) {
     LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2: %m");
    }
    ASSERT_EQ(expected_exit_size, rc) {
     LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2"
       ": exit stop mismatch");
    }
    check_psi_exit(_metadata, &info, set_exit,
      "PTRACE_GET_SYSCALL_INFO #2");
   }
   break;

  default:
   LOG_KILL_TRACEE("unexpected stop signal %u",
     WSTOPSIG(status));
   abort();
  }

  ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, tracee_pid, 0, 0)) {
   LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
  }
 }

 ASSERT_EQ(ptrace_stop, ARRAY_SIZE(si) * 2);
}

TEST_HARNESS_MAIN

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.