All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping
@ 2026-07-04  8:33 Renzo Davoli
  2026-07-04  8:33 ` [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
  2026-07-04  8:33 ` [PATCH 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli
  0 siblings, 2 replies; 5+ messages in thread
From: Renzo Davoli @ 2026-07-04  8:33 UTC (permalink / raw)
  To: linux-kernel
  Cc: Renzo Davoli, Andrew Morton, Oleg Nesterov, Shuah Khan,
	Alexey Gladkov, Eugene Syromyatnikov, Davide Berardi,
	strace-devel, Dmitry V . Levin

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 tracing a system call the tracer can get the system call information
in a portable manner using the ptrace tag PTRACE_GET_SYSCALL_INFO.
The op returned in struct ptrace_syscall_info can be
PTRACE_SYSCALL_INFO_ENTRY or PTRACE_SYSCALL_INFO_SECCOMP depending on
the way the system call was "captured".

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.

Patch 1/5 introduces adds a new feature to solve the problem.

When the tracer retrieves a ptrace_syscall_info structure with op ==
PTRACE_SYSCALL_INFO_SECCOMP or PTRACE_SYSCALL_INFO_ENTRY, 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 is following soon)

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                               |  23 ++-
 .../selftests/ptrace/set_syscall_info.c       | 176 +++++++++++++++++-
 2 files changed, 193 insertions(+), 6 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support
  2026-07-04  8:33 [PATCH 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping Renzo Davoli
@ 2026-07-04  8:33 ` Renzo Davoli
  2026-07-04 11:17   ` Oleg Nesterov
  2026-07-04  8:33 ` [PATCH 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli
  1 sibling, 1 reply; 5+ messages in thread
From: Renzo Davoli @ 2026-07-04  8:33 UTC (permalink / raw)
  To: linux-kernel
  Cc: Renzo Davoli, Andrew Morton, Oleg Nesterov, Shuah Khan,
	Alexey Gladkov, Eugene Syromyatnikov, Davide Berardi,
	strace-devel, Dmitry V . Levin

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 or PTRACE_SYSCALL_INFO_ENTRY, 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 | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d041645d9d17..3877624daf4e 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;
 
@@ -1116,6 +1116,9 @@ ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs,
 	else
 		syscall_set_return_value(child, regs, 0, rval);
 
+	if (skip_syscall)
+		syscall_set_nr(child, regs, -1);
+
 	return 0;
 }
 
@@ -1125,6 +1128,7 @@ 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;
+	bool skip_syscall = false;
 
 	if (user_size < sizeof(info))
 		return -EINVAL;
@@ -1141,15 +1145,24 @@ 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 or PTRACE_SYSCALL_INFO_ENTRY can be changed
+	 * to PTRACE_SYSCALL_INFO_EXIT to skip the system call
+	 */
+
+	if (ptrace_get_syscall_info_op(child) != info.op) {
+		if (info.op != PTRACE_SYSCALL_INFO_EXIT)
+			return -EINVAL;
+		skip_syscall = true;
+	}
 
 	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] 5+ messages in thread

* [PATCH 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping
  2026-07-04  8:33 [PATCH 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping Renzo Davoli
  2026-07-04  8:33 ` [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
@ 2026-07-04  8:33 ` Renzo Davoli
  1 sibling, 0 replies; 5+ messages in thread
From: Renzo Davoli @ 2026-07-04  8:33 UTC (permalink / raw)
  To: linux-kernel
  Cc: Renzo Davoli, Andrew Morton, Oleg Nesterov, Shuah Khan,
	Alexey Gladkov, Eugene Syromyatnikov, Davide Berardi,
	strace-devel, Dmitry V . Levin

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       | 176 +++++++++++++++++-
 1 file changed, 175 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..bcc867b627cd 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,151 @@ 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;
+			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] 5+ messages in thread

* Re: [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support
  2026-07-04  8:33 ` [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
@ 2026-07-04 11:17   ` Oleg Nesterov
  2026-07-04 14:25     ` Renzo Davoli
  0 siblings, 1 reply; 5+ messages in thread
From: Oleg Nesterov @ 2026-07-04 11:17 UTC (permalink / raw)
  To: Renzo Davoli
  Cc: linux-kernel, Andrew Morton, Shuah Khan, Alexey Gladkov,
	Eugene Syromyatnikov, Davide Berardi, strace-devel,
	Dmitry V . Levin

On 07/04, Renzo Davoli wrote:
>
> -	/* 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 or PTRACE_SYSCALL_INFO_ENTRY can be changed
> +	 * to PTRACE_SYSCALL_INFO_EXIT to skip the system call
> +	 */
> +
> +	if (ptrace_get_syscall_info_op(child) != info.op) {
> +		if (info.op != PTRACE_SYSCALL_INFO_EXIT)
> +			return -EINVAL;
> +		skip_syscall = true;
> +	}

Ah... I forgot that ptrace_get_syscall_info_op() can return _NONE.

We should not allow _NONE -> _EXIT transition.

Oleg.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support
  2026-07-04 11:17   ` Oleg Nesterov
@ 2026-07-04 14:25     ` Renzo Davoli
  0 siblings, 0 replies; 5+ messages in thread
From: Renzo Davoli @ 2026-07-04 14:25 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: linux-kernel, Andrew Morton, Shuah Khan, Alexey Gladkov,
	Eugene Syromyatnikov, Davide Berardi, strace-devel,
	Dmitry V . Levin

You are right, fixing in v2.
renzo

On Sat, Jul 04, 2026 at 01:17:48PM +0200, Oleg Nesterov wrote:
> On 07/04, Renzo Davoli wrote:
> >
> > -	/* 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 or PTRACE_SYSCALL_INFO_ENTRY can be changed
> > +	 * to PTRACE_SYSCALL_INFO_EXIT to skip the system call
> > +	 */
> > +
> > +	if (ptrace_get_syscall_info_op(child) != info.op) {
> > +		if (info.op != PTRACE_SYSCALL_INFO_EXIT)
> > +			return -EINVAL;
> > +		skip_syscall = true;
> > +	}
> 
> Ah... I forgot that ptrace_get_syscall_info_op() can return _NONE.
> 
> We should not allow _NONE -> _EXIT transition.
> 
> Oleg.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-04 14:25 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-04  8:33 [PATCH 0/2] ptrace_set_syscall_info: add support for seccomp syscall skipping Renzo Davoli
2026-07-04  8:33 ` [PATCH 1/2] ptrace: PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
2026-07-04 11:17   ` Oleg Nesterov
2026-07-04 14:25     ` Renzo Davoli
2026-07-04  8:33 ` [PATCH 2/2] selftests/ptrace: add a test case for PTRACE_SYSCALL_INFO syscall skipping Renzo Davoli

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.