The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [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

* [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 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

* 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