* [PATCH v3 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping
@ 2026-07-07 11:21 Renzo Davoli
2026-07-07 11:21 ` [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
2026-07-07 11:21 ` [PATCH v3 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli
0 siblings, 2 replies; 7+ messages in thread
From: Renzo Davoli @ 2026-07-07 11:21 UTC (permalink / raw)
To: linux-kernel
Cc: Renzo Davoli, Andrew Morton, Oleg Nesterov, Shuah Khan,
Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger,
Davide Berardi, strace-devel
PTRACE_SET_SYSCALL_INFO is a generic ptrace API that complements
PTRACE_GET_SYSCALL_INFO by allowing a tracer to modify details of a
system call in which the tracee is currently blocked.
The API is designed to let tracers inspect and modify system call
information in a simple, architecture-agnostic manner.
The current implementation only supports modifying the subset of
system call information needed by strace: the system call number,
arguments, and return value.
This patch set extends PTRACE_SET_SYSCALL_INFO with support for
skipping a system call.
When a seccomp filter returns SECCOMP_RET_TRACE, the tracer receives,
via PTRACE_GET_SYSCALL_INFO, a struct ptrace_syscall_info with
op == PTRACE_SYSCALL_INFO_SECCOMP.
The tracer can skip the system call by setting the system call number
to -1. However, the current PTRACE_SET_SYSCALL_INFO interface does not
provide a way to specify the return value or error code that should be
reported to the tracee after skipping the call.
This patchset adds a new feature to solve the problem.
When the tracer retrieves a ptrace_syscall_info structure with op ==
PTRACE_SYSCALL_INFO_SECCOMP it may choose to skip the system call by changing
op to PTRACE_SYSCALL_INFO_EXIT and populating the exit union fields (rval and
is_error) to define the return value and error status for the tracee.
This patchset is a new version of the proposed patchset entitled:
ptrace_set_syscall_info: add support for seccomp syscall skipping and
instruction pointer modification
The patchset has been split in two:
syscall skipping(this)
instruction pointer modification (it will be updated soon)
Changes in v3:
* restrict the syscall skipping feature to PTRACE_SYSCALL_INFO_SECCOMP
Changes in v2:
bugfix: _NONE -> _EXIT transition was erroneously permitted
Changes since the previous patchset v2:
* bugfix: skip_syscall init value
* fix comments
Changes in (previous patchset) v2:
* use PTRACE_SYSCALL_INFO_EXIT instead of a new tag
* fixed most of the comments from sashiko.dev
Renzo Davoli (2):
ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support
selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall
skipping
kernel/ptrace.c | 27 ++-
.../selftests/ptrace/set_syscall_info.c | 179 +++++++++++++++++-
2 files changed, 200 insertions(+), 6 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support 2026-07-07 11:21 [PATCH v3 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping Renzo Davoli @ 2026-07-07 11:21 ` Renzo Davoli 2026-07-07 16:35 ` Oleg Nesterov 2026-07-07 17:19 ` Dmitry V. Levin 2026-07-07 11:21 ` [PATCH v3 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli 1 sibling, 2 replies; 7+ messages in thread From: Renzo Davoli @ 2026-07-07 11:21 UTC (permalink / raw) To: linux-kernel Cc: Renzo Davoli, Andrew Morton, Oleg Nesterov, Shuah Khan, Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger, Davide Berardi, strace-devel This patch extends PTRACE_SET_SYSCALL_INFO with support for skipping a system call triggered via seccomp. When the tracer retrieves a ptrace_syscall_info structure with op == PTRACE_SYSCALL_INFO_SECCOMP, it may choose to skip the system call by changing op to PTRACE_SYSCALL_INFO_EXIT and populating the exit union fields (rval and is_error) to define the return value and error status for the tracee. Signed-off-by: Renzo Davoli <renzo@cs.unibo.it> --- kernel/ptrace.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index d041645d9d17..8ea807981390 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -1099,7 +1099,7 @@ ptrace_set_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs, static int ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs, - struct ptrace_syscall_info *info) + struct ptrace_syscall_info *info, bool skip_syscall) { long rval = info->exit.rval; @@ -1111,6 +1111,9 @@ ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs, if (rval != info->exit.rval) return -ERANGE; + if (skip_syscall) + syscall_set_nr(child, regs, -1); + if (info->exit.is_error) syscall_set_return_value(child, regs, rval, 0); else @@ -1125,6 +1128,8 @@ ptrace_set_syscall_info(struct task_struct *child, unsigned long user_size, { struct pt_regs *regs = task_pt_regs(child); struct ptrace_syscall_info info; + int child_op; + bool skip_syscall = false; if (user_size < sizeof(info)) return -EINVAL; @@ -1141,15 +1146,27 @@ ptrace_set_syscall_info(struct task_struct *child, unsigned long user_size, if (info.flags || info.reserved) return -EINVAL; - /* Changing the type of the system call stop is not supported yet. */ - if (ptrace_get_syscall_info_op(child) != info.op) - return -EINVAL; + /* + * Changing the type of the system call stop is not allowed, with the + * following exception: + * PTRACE_SYSCALL_INFO_SECCOMP can be changed to PTRACE_SYSCALL_INFO_EXIT + * to skip the system call + */ + + child_op = ptrace_get_syscall_info_op(child); + if (child_op != info.op) { + if (info.op == PTRACE_SYSCALL_INFO_EXIT && + child_op == PTRACE_SYSCALL_INFO_SECCOMP) + skip_syscall = true; + else + return -EINVAL; + } switch (info.op) { case PTRACE_SYSCALL_INFO_ENTRY: return ptrace_set_syscall_info_entry(child, regs, &info); case PTRACE_SYSCALL_INFO_EXIT: - return ptrace_set_syscall_info_exit(child, regs, &info); + return ptrace_set_syscall_info_exit(child, regs, &info, skip_syscall); case PTRACE_SYSCALL_INFO_SECCOMP: return ptrace_set_syscall_info_seccomp(child, regs, &info); default: -- 2.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support 2026-07-07 11:21 ` [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli @ 2026-07-07 16:35 ` Oleg Nesterov 2026-07-07 17:19 ` Dmitry V. Levin 1 sibling, 0 replies; 7+ messages in thread From: Oleg Nesterov @ 2026-07-07 16:35 UTC (permalink / raw) To: Renzo Davoli, Dmitry V . Levin Cc: linux-kernel, Andrew Morton, Shuah Khan, Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger, Davide Berardi, strace-devel On 07/07, Renzo Davoli wrote: > > + child_op = ptrace_get_syscall_info_op(child); > + if (child_op != info.op) { > + if (info.op == PTRACE_SYSCALL_INFO_EXIT && > + child_op == PTRACE_SYSCALL_INFO_SECCOMP) > + skip_syscall = true; > + else > + return -EINVAL; OK, I have to agree, we can't allow _ENTRY -> _EXIT transition right now. Signed-off-by: Oleg Nesterov <oleg@redhat.com> --------------------------------------------------------------------------- Hopefully we can allow _ENTRY -> _EXIT later, when/if we have some help from arch/ maintainers. Although I think that the much more likely case is when Dmitry who understands _every_ arch/ will have time/motivation to make the neccessary changes ;) Hmm... but you removed him from CC list, please don't do this. Add him back. And... As I said, I agree with any naming. But somehow I don't like "child_op". Again, I won't argue, but if you will have any reason to send V4, please think about something else. "reported_op" or just "op". To me "child_op" looks a bit confusing, but this is minor/subjective, feel free to ignore. Oleg. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support 2026-07-07 11:21 ` [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli 2026-07-07 16:35 ` Oleg Nesterov @ 2026-07-07 17:19 ` Dmitry V. Levin 2026-07-07 17:44 ` Oleg Nesterov 1 sibling, 1 reply; 7+ messages in thread From: Dmitry V. Levin @ 2026-07-07 17:19 UTC (permalink / raw) To: Renzo Davoli Cc: linux-kernel, Andrew Morton, Oleg Nesterov, Shuah Khan, Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger, Davide Berardi, strace-devel On Tue, Jul 07, 2026 at 01:21:06PM +0200, Renzo Davoli wrote: > This patch extends PTRACE_SET_SYSCALL_INFO with support for skipping a system > call triggered via seccomp. > > When the tracer retrieves a ptrace_syscall_info structure with op == > PTRACE_SYSCALL_INFO_SECCOMP, it may choose to skip the system call by changing > op to PTRACE_SYSCALL_INFO_EXIT and populating the exit union fields (rval and > is_error) to define the return value and error status for the tracee. I suggest changing the wording of the commit message as follows: ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support Extend PTRACE_SET_SYSCALL_INFO to support skipping a system call triggered via seccomp. When a tracer retrieves a ptrace_syscall_info structure with 'op' set to PTRACE_SYSCALL_INFO_SECCOMP, it can now choose to skip the system call. To do this, the tracer changes 'op' to PTRACE_SYSCALL_INFO_EXIT and populates the exit union fields (rval and is_error) to define the return value and error status for the tracee. > Signed-off-by: Renzo Davoli <renzo@cs.unibo.it> > --- > kernel/ptrace.c | 27 ++++++++++++++++++++++----- > 1 file changed, 22 insertions(+), 5 deletions(-) > > diff --git a/kernel/ptrace.c b/kernel/ptrace.c > index d041645d9d17..8ea807981390 100644 > --- a/kernel/ptrace.c > +++ b/kernel/ptrace.c > @@ -1099,7 +1099,7 @@ ptrace_set_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs, > > static int > ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs, > - struct ptrace_syscall_info *info) > + struct ptrace_syscall_info *info, bool skip_syscall) > { > long rval = info->exit.rval; > > @@ -1111,6 +1111,9 @@ ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs, > if (rval != info->exit.rval) > return -ERANGE; > > + if (skip_syscall) > + syscall_set_nr(child, regs, -1); > + > if (info->exit.is_error) > syscall_set_return_value(child, regs, rval, 0); > else > @@ -1125,6 +1128,8 @@ ptrace_set_syscall_info(struct task_struct *child, unsigned long user_size, > { > struct pt_regs *regs = task_pt_regs(child); > struct ptrace_syscall_info info; > + int child_op; > + bool skip_syscall = false; > > if (user_size < sizeof(info)) > return -EINVAL; > @@ -1141,15 +1146,27 @@ ptrace_set_syscall_info(struct task_struct *child, unsigned long user_size, > if (info.flags || info.reserved) > return -EINVAL; > > - /* Changing the type of the system call stop is not supported yet. */ > - if (ptrace_get_syscall_info_op(child) != info.op) > - return -EINVAL; > + /* > + * Changing the type of the system call stop is not allowed, with the > + * following exception: > + * PTRACE_SYSCALL_INFO_SECCOMP can be changed to PTRACE_SYSCALL_INFO_EXIT > + * to skip the system call > + */ > + > + child_op = ptrace_get_syscall_info_op(child); Apparently, Oleg is not quite happy with the name child_op, so let's rename it to reported_op. > + if (child_op != info.op) { > + if (info.op == PTRACE_SYSCALL_INFO_EXIT && > + child_op == PTRACE_SYSCALL_INFO_SECCOMP) Indentation of the last line looks somewhat unusual. > + skip_syscall = true; > + else > + return -EINVAL; > + } > switch (info.op) { > case PTRACE_SYSCALL_INFO_ENTRY: > return ptrace_set_syscall_info_entry(child, regs, &info); > case PTRACE_SYSCALL_INFO_EXIT: > - return ptrace_set_syscall_info_exit(child, regs, &info); > + return ptrace_set_syscall_info_exit(child, regs, &info, skip_syscall); > case PTRACE_SYSCALL_INFO_SECCOMP: > return ptrace_set_syscall_info_seccomp(child, regs, &info); > default: Feel free to add: Reviewed-by: Dmitry V. Levin <ldv@strace.io> -- ldv ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support 2026-07-07 17:19 ` Dmitry V. Levin @ 2026-07-07 17:44 ` Oleg Nesterov 0 siblings, 0 replies; 7+ messages in thread From: Oleg Nesterov @ 2026-07-07 17:44 UTC (permalink / raw) To: Dmitry V. Levin Cc: Renzo Davoli, linux-kernel, Andrew Morton, Shuah Khan, Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger, Davide Berardi, strace-devel On 07/07, Dmitry V. Levin wrote: > > I suggest changing the wording of the commit message as follows: > > [... snip... ] Agreed! and perhaps it makes sense to add a short note (or just a link to the previous discussions) to explain why we can't do the same when op == ENTRY. But this is minor. Oleg. ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping 2026-07-07 11:21 [PATCH v3 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping Renzo Davoli 2026-07-07 11:21 ` [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli @ 2026-07-07 11:21 ` Renzo Davoli 2026-07-08 7:47 ` Dmitry V. Levin 1 sibling, 1 reply; 7+ messages in thread From: Renzo Davoli @ 2026-07-07 11:21 UTC (permalink / raw) To: linux-kernel Cc: Renzo Davoli, Andrew Morton, Oleg Nesterov, Shuah Khan, Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger, Davide Berardi, strace-devel Check whether PTRACE_SYSCALL_INFO syscall skiping semantics implemented in the kernel matches userspace expectations. Signed-off-by: Renzo Davoli <renzo@cs.unibo.it> --- .../selftests/ptrace/set_syscall_info.c | 179 +++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/ptrace/set_syscall_info.c b/tools/testing/selftests/ptrace/set_syscall_info.c index 1cc411a41cd6..af3c908b0d68 100644 --- a/tools/testing/selftests/ptrace/set_syscall_info.c +++ b/tools/testing/selftests/ptrace/set_syscall_info.c @@ -11,9 +11,16 @@ #include <err.h> #include <fcntl.h> #include <signal.h> +#include <stdlib.h> +#include <stddef.h> #include <asm/unistd.h> +#include <sys/prctl.h> #include <linux/types.h> #include <linux/ptrace.h> +#include <linux/filter.h> +#include <linux/seccomp.h> +#include <linux/prctl.h> + #if defined(_MIPS_SIM) && _MIPS_SIM == _MIPS_SIM_NABI32 /* @@ -36,6 +43,7 @@ struct si_exit { static unsigned int ptrace_stop; static pid_t tracee_pid; +static pid_t tracer_pid; static int kill_tracee(pid_t pid) @@ -64,6 +72,25 @@ sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data) ptrace_stop, ##__VA_ARGS__); \ } while (0) +static int sys_seccomp(unsigned int operation, unsigned int flags, void *args) +{ + return syscall(__NR_seccomp, operation, flags, args); +} + +static struct sock_filter seccomp_filter[] = { + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), + + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_restart_syscall, 0, 1), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), + + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE), +}; + +static struct sock_fprog seccomp_prog = { + .filter = seccomp_filter, + .len = ARRAY_SIZE(seccomp_filter) +}; + static void check_psi_entry(struct __test_metadata *_metadata, const struct ptrace_syscall_info *info, @@ -128,7 +155,6 @@ check_psi_exit(struct __test_metadata *_metadata, TEST(set_syscall_info) { - const pid_t tracer_pid = getpid(); const kernel_ulong_t dummy[] = { (kernel_ulong_t) 0xdad0bef0bad0fed0ULL, (kernel_ulong_t) 0xdad1bef1bad1fed1ULL, @@ -138,6 +164,7 @@ TEST(set_syscall_info) (kernel_ulong_t) 0xdad5bef5bad5fed5ULL, }; int splice_in[2], splice_out[2]; + tracer_pid = getpid(); ASSERT_EQ(0, pipe(splice_in)); ASSERT_EQ(0, pipe(splice_out)); @@ -516,4 +543,154 @@ TEST(set_syscall_info) ASSERT_EQ(ptrace_stop, ARRAY_SIZE(si) * 2); } +TEST(set_syscall_info_seccomp) +{ + tracer_pid = getpid(); + tracee_pid = fork(); + + ASSERT_LE(0, tracee_pid) { + TH_LOG("fork: %m"); + } + + /* tracee */ + if (tracee_pid == 0) { + 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"); + } + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + TH_LOG("prctl: %m"); + _exit(1); + } + ASSERT_EQ(0, sys_seccomp(SECCOMP_SET_MODE_FILTER, 0, + (void *) &seccomp_prog)) { + TH_LOG("seccomp: %m"); + _exit(1); + } + + /* run getpid unmodified */ + ASSERT_EQ(tracee_pid, getpid()) { + TH_LOG("getpid seccomp unchanged: %m"); + _exit(1); + } + + /* run getppid instead of getpid */ + ASSERT_EQ(tracer_pid, getpid()) { + TH_LOG("getpid seccomp nr changes: %m"); + _exit(1); + } + + /* skip getpid and return 42 */ + ASSERT_EQ(42, getpid()) { + TH_LOG("getpid skip set return value changes: %m"); + _exit(1); + } + _exit(0); + } + + int status; + + /* tracer */ + ASSERT_LE(0, waitpid(-1,&status,0)) { + LOG_KILL_TRACEE("waitpid: %m"); + } + + ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, tracee_pid, 0, PTRACE_O_TRACESECCOMP | PTRACE_O_TRACESYSGOOD)) + LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m"); + + ASSERT_EQ(0, sys_ptrace(PTRACE_CONT, tracee_pid, 0, 0)) { + LOG_KILL_TRACEE("PTRACE_CONT: %m"); + } + + while (1) { + 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)) { + LOG_KILL_TRACEE("unexpected wait status %#x", status); + } + + if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { + struct ptrace_syscall_info info = { + .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */ + }; + size_t info_size = sizeof(info); + + ASSERT_LT(0, sys_ptrace(PTRACE_GET_SYSCALL_INFO, tracee_pid, info_size, (uintptr_t) &info)) { + LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m"); + }; + ASSERT_EQ(PTRACE_SYSCALL_INFO_SECCOMP, info.op) { + LOG_KILL_TRACEE("entry op mismatch: %m"); + } + ASSERT_TRUE(info.arch) { + LOG_KILL_TRACEE("entry arch mismatch: %m"); + } + ASSERT_TRUE(info.instruction_pointer) { + LOG_KILL_TRACEE("entry instruction_pointer mismatch: %m"); + } + ASSERT_TRUE(info.stack_pointer) { + LOG_KILL_TRACEE("entry stack_pointer mismatch: %m"); + } + + switch (ptrace_stop) { + case 0: ASSERT_EQ(__NR_getpid, info.seccomp.nr) { + LOG_KILL_TRACEE("step %d nr __NR_getpid mismatch: %m", ptrace_stop); + } + ptrace_stop++; + break; + case 1: ASSERT_EQ(__NR_getpid, info.seccomp.nr) { + LOG_KILL_TRACEE("step %d nr __NR_getpid mismatch: %m", ptrace_stop); + } + info.seccomp.nr = __NR_getppid; + ptrace_stop++; + break; + case 2: ASSERT_EQ(__NR_getpid, info.seccomp.nr) { + LOG_KILL_TRACEE("step %d nr __NR_getpid mismatch: %m", ptrace_stop); + } + info.op = PTRACE_SYSCALL_INFO_EXIT; + info.exit.rval = 42; + info.exit.is_error = 0; + ptrace_stop++; + break; + case 3: ASSERT_EQ(__NR_exit_group, info.seccomp.nr) { + LOG_KILL_TRACEE("step %d nr __NR_exit_group mismatch: %m", ptrace_stop); + } + break; + default: + LOG_KILL_TRACEE("unexpected system call: %m"); + break; + + } + ASSERT_EQ(0,sys_ptrace(PTRACE_SET_SYSCALL_INFO, tracee_pid, info_size, (uintptr_t) &info)) { + LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m"); + } + + ASSERT_EQ(0,sys_ptrace(PTRACE_CONT, tracee_pid, 0, 0)) { + LOG_KILL_TRACEE("PTRACE_CONT: %m"); + } + } else { + LOG_KILL_TRACEE("unexpected signal: %m"); + } + } +} + TEST_HARNESS_MAIN -- 2.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping 2026-07-07 11:21 ` [PATCH v3 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli @ 2026-07-08 7:47 ` Dmitry V. Levin 0 siblings, 0 replies; 7+ messages in thread From: Dmitry V. Levin @ 2026-07-08 7:47 UTC (permalink / raw) To: Renzo Davoli Cc: linux-kernel, Andrew Morton, Oleg Nesterov, Shuah Khan, Alexey Gladkov, Eugene Syromyatnikov, Mike Frysinger, Davide Berardi, strace-devel On Tue, Jul 07, 2026 at 01:21:07PM +0200, Renzo Davoli wrote: > Check whether PTRACE_SYSCALL_INFO syscall skiping semantics implemented in the > kernel matches userspace expectations. typo: skiping > > Signed-off-by: Renzo Davoli <renzo@cs.unibo.it> > --- > .../selftests/ptrace/set_syscall_info.c | 179 +++++++++++++++++- > 1 file changed, 178 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/ptrace/set_syscall_info.c b/tools/testing/selftests/ptrace/set_syscall_info.c > index 1cc411a41cd6..af3c908b0d68 100644 > --- a/tools/testing/selftests/ptrace/set_syscall_info.c > +++ b/tools/testing/selftests/ptrace/set_syscall_info.c > @@ -11,9 +11,16 @@ > #include <err.h> > #include <fcntl.h> > #include <signal.h> > +#include <stdlib.h> > +#include <stddef.h> > #include <asm/unistd.h> > +#include <sys/prctl.h> > #include <linux/types.h> > #include <linux/ptrace.h> > +#include <linux/filter.h> > +#include <linux/seccomp.h> > +#include <linux/prctl.h> > + Even though an extra blank line is not a problem, it's a little distraction from the change. > #if defined(_MIPS_SIM) && _MIPS_SIM == _MIPS_SIM_NABI32 > /* > @@ -36,6 +43,7 @@ struct si_exit { > > static unsigned int ptrace_stop; > static pid_t tracee_pid; > +static pid_t tracer_pid; > > static int > kill_tracee(pid_t pid) > @@ -64,6 +72,25 @@ sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data) > ptrace_stop, ##__VA_ARGS__); \ > } while (0) > > +static int sys_seccomp(unsigned int operation, unsigned int flags, void *args) > +{ > + return syscall(__NR_seccomp, operation, flags, args); > +} > + > +static struct sock_filter seccomp_filter[] = { > + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), > + > + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_restart_syscall, 0, 1), > + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), > + > + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE), > +}; > + > +static struct sock_fprog seccomp_prog = { > + .filter = seccomp_filter, > + .len = ARRAY_SIZE(seccomp_filter) > +}; > + > static void > check_psi_entry(struct __test_metadata *_metadata, > const struct ptrace_syscall_info *info, > @@ -128,7 +155,6 @@ check_psi_exit(struct __test_metadata *_metadata, > > TEST(set_syscall_info) > { > - const pid_t tracer_pid = getpid(); > const kernel_ulong_t dummy[] = { > (kernel_ulong_t) 0xdad0bef0bad0fed0ULL, > (kernel_ulong_t) 0xdad1bef1bad1fed1ULL, > @@ -138,6 +164,7 @@ TEST(set_syscall_info) > (kernel_ulong_t) 0xdad5bef5bad5fed5ULL, > }; > int splice_in[2], splice_out[2]; > + tracer_pid = getpid(); > > ASSERT_EQ(0, pipe(splice_in)); > ASSERT_EQ(0, pipe(splice_out)); > @@ -516,4 +543,154 @@ TEST(set_syscall_info) > ASSERT_EQ(ptrace_stop, ARRAY_SIZE(si) * 2); > } > > +TEST(set_syscall_info_seccomp) > +{ > + tracer_pid = getpid(); > + tracee_pid = fork(); > + > + ASSERT_LE(0, tracee_pid) { > + TH_LOG("fork: %m"); > + } > + > + /* tracee */ > + if (tracee_pid == 0) { > + 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"); > + } > + > + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { > + TH_LOG("prctl: %m"); > + _exit(1); > + } > + ASSERT_EQ(0, sys_seccomp(SECCOMP_SET_MODE_FILTER, 0, > + (void *) &seccomp_prog)) { > + TH_LOG("seccomp: %m"); > + _exit(1); > + } > + > + /* run getpid unmodified */ > + ASSERT_EQ(tracee_pid, getpid()) { > + TH_LOG("getpid seccomp unchanged: %m"); > + _exit(1); > + } > + > + /* run getppid instead of getpid */ > + ASSERT_EQ(tracer_pid, getpid()) { > + TH_LOG("getpid seccomp nr changes: %m"); > + _exit(1); > + } > + > + /* skip getpid and return 42 */ > + ASSERT_EQ(42, getpid()) { > + TH_LOG("getpid skip set return value changes: %m"); > + _exit(1); > + } Since ASSERT_EQ aborts in case of assertion failure, why do you need these explicit _exit(1) calls here? > + _exit(0); > + } > + > + int status; > + > + /* tracer */ > + ASSERT_LE(0, waitpid(-1,&status,0)) { Missing spaces after comma: waitpid(-1,&status,0) -> waitpid(-1, &status, 0) > + LOG_KILL_TRACEE("waitpid: %m"); > + } > + > + ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, tracee_pid, 0, PTRACE_O_TRACESECCOMP | PTRACE_O_TRACESYSGOOD)) > + LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m"); Missing braces on ASSERT_EQ. > + > + ASSERT_EQ(0, sys_ptrace(PTRACE_CONT, tracee_pid, 0, 0)) { > + LOG_KILL_TRACEE("PTRACE_CONT: %m"); > + } > + > + while (1) { I suggest to replace while (1) { with for (ptrace_stop = 0; ; ++ptrace_stop) { and don't bother changing ptrace_stop inside the loop. > + 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)) { > + LOG_KILL_TRACEE("unexpected wait status %#x", status); > + } > + > + if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { I suggest turning this into assertion, e.g. ASSERT_EQ(status >> 8, SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)) { LOG_KILL_TRACEE("unexpected stop, wait status %#x", status); } > + struct ptrace_syscall_info info = { > + .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */ > + }; > + size_t info_size = sizeof(info); > + > + ASSERT_LT(0, sys_ptrace(PTRACE_GET_SYSCALL_INFO, tracee_pid, info_size, (uintptr_t) &info)) { > + LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m"); > + }; Stray semicolon after the closing brace. > + ASSERT_EQ(PTRACE_SYSCALL_INFO_SECCOMP, info.op) { > + LOG_KILL_TRACEE("entry op mismatch: %m"); > + } > + ASSERT_TRUE(info.arch) { > + LOG_KILL_TRACEE("entry arch mismatch: %m"); > + } > + ASSERT_TRUE(info.instruction_pointer) { > + LOG_KILL_TRACEE("entry instruction_pointer mismatch: %m"); > + } > + ASSERT_TRUE(info.stack_pointer) { > + LOG_KILL_TRACEE("entry stack_pointer mismatch: %m"); > + } I suggest adding an extra check here: ASSERT_LT(ptrace_stop, 4) { LOG_KILL_TRACEE("ptrace stop overflow"); } > + switch (ptrace_stop) { > + case 0: ASSERT_EQ(__NR_getpid, info.seccomp.nr) { Trailing statements on case lines: "case 0: ASSERT_EQ(...)" should have the ASSERT on the next line. > + LOG_KILL_TRACEE("step %d nr __NR_getpid mismatch: %m", ptrace_stop); As LOG_KILL_TRACEE() prints ptrace_stop itself, let's keep it simple, e.g. LOG_KILL_TRACEE("syscall nr mismatch"); > + } > + ptrace_stop++; > + break; > + case 1: ASSERT_EQ(__NR_getpid, info.seccomp.nr) { > + LOG_KILL_TRACEE("step %d nr __NR_getpid mismatch: %m", ptrace_stop); > + } > + info.seccomp.nr = __NR_getppid; > + ptrace_stop++; > + break; > + case 2: ASSERT_EQ(__NR_getpid, info.seccomp.nr) { > + LOG_KILL_TRACEE("step %d nr __NR_getpid mismatch: %m", ptrace_stop); > + } > + info.op = PTRACE_SYSCALL_INFO_EXIT; > + info.exit.rval = 42; > + info.exit.is_error = 0; > + ptrace_stop++; > + break; > + case 3: ASSERT_EQ(__NR_exit_group, info.seccomp.nr) { > + LOG_KILL_TRACEE("step %d nr __NR_exit_group mismatch: %m", ptrace_stop); > + } > + break; > + default: > + LOG_KILL_TRACEE("unexpected system call: %m"); > + break; > + > + } Given that all info.seccomp.nr assertions are essentially identical, this could be simplified into something like this: const unsigned int expected_nr[] = { __NR_getpid, __NR_getpid, __NR_getpid, __NR_exit_group }; ASSERT_LT(ptrace_stop, ARRAY_SIZE(expected_nr)) { LOG_KILL_TRACEE("ptrace stop overflow"); } ASSERT_EQ(info.seccomp.nr, expected_nr[ptrace_stop]) { LOG_KILL_TRACEE("syscall nr mismatch"); } switch (ptrace_stop) { case 0: case 3: break; case 1: info.seccomp.nr = __NR_getppid; break; case 2: info.op = PTRACE_SYSCALL_INFO_EXIT; info.exit.rval = 42; info.exit.is_error = 0; break; } > + ASSERT_EQ(0,sys_ptrace(PTRACE_SET_SYSCALL_INFO, tracee_pid, info_size, (uintptr_t) &info)) { Missing space after comma. > + LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m"); > + } > + > + ASSERT_EQ(0,sys_ptrace(PTRACE_CONT, tracee_pid, 0, 0)) { Likewise. -- ldv ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-08 7:47 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-07 11:21 [PATCH v3 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping Renzo Davoli 2026-07-07 11:21 ` [PATCH v3 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli 2026-07-07 16:35 ` Oleg Nesterov 2026-07-07 17:19 ` Dmitry V. Levin 2026-07-07 17:44 ` Oleg Nesterov 2026-07-07 11:21 ` [PATCH v3 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli 2026-07-08 7:47 ` Dmitry V. Levin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox