* [PATCH v7 0/2] PTRACE_SET_SYSCALL_INFO: add support for seccomp syscall skipping
@ 2026-08-31 8:27 Renzo Davoli
2026-08-31 8:27 ` [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
2026-08-31 8:27 ` [PATCH v7 2/2] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO syscall skipping Renzo Davoli
0 siblings, 2 replies; 5+ messages in thread
From: Renzo Davoli @ 2026-08-31 8:27 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, Thomas Bogendoerfer
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 extends 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.
System call suppression via PTRACE_SYSCALL_INFO_ENTRY is currently not
implemented. On some architectures (e.g. MIPS), when a system call is
skipped by setting the syscall number to -1 at the entry stop, the
architecture entry path unconditionally overwrites the return value
register with -ENOSYS, clobbering any custom return value set by the
tracer at the entry stop.
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 v7:
* rebased to Linux 7.3-rc1
Changes in v6:
* selftests/ptrace/set_syscall_info.c: improve messages in case of failure.
Changes in v5:
* reworded the explanation for not supporting PTRACE_SYSCALL_INFO_ENTRY yet.
* selftests/ptrace: removed a redundant check.
Changes in v4:
* Reworded the commit messages for clarity.
* Renamed the local variable child_op to op in ptrace_set_syscall_info()
* Clean up and improve the coding style of selftests/ptrace/set_syscall_info.c
(suggested by Dmitry V. Levin)
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: add PTRACE_SET_SYSCALL_INFO syscall skipping support
selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO syscall
skipping
kernel/ptrace.c | 27 ++-
.../selftests/ptrace/set_syscall_info.c | 184 +++++++++++++++++-
2 files changed, 205 insertions(+), 6 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support
2026-08-31 8:27 [PATCH v7 0/2] PTRACE_SET_SYSCALL_INFO: add support for seccomp syscall skipping Renzo Davoli
@ 2026-08-31 8:27 ` Renzo Davoli
2026-08-31 13:20 ` Oleg Nesterov
2026-08-31 8:27 ` [PATCH v7 2/2] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO syscall skipping Renzo Davoli
1 sibling, 1 reply; 5+ messages in thread
From: Renzo Davoli @ 2026-08-31 8:27 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, Thomas Bogendoerfer
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.
System call suppression via PTRACE_SYSCALL_INFO_ENTRY is currently not
implemented. On some architectures (e.g. MIPS), when a system call is
skipped by setting the syscall number to -1 at the entry stop, the
architecture entry path unconditionally overwrites the return value
register with -ENOSYS, clobbering any custom return value set by the
tracer at the entry stop.
Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Dmitry V. Levin <ldv@strace.io>
---
kernel/ptrace.c | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d041645d9d17..64fd1b455297 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 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
+ */
+
+ op = ptrace_get_syscall_info_op(child);
+ if (op != info.op) {
+ if (info.op == PTRACE_SYSCALL_INFO_EXIT &&
+ 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.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v7 2/2] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO syscall skipping
2026-08-31 8:27 [PATCH v7 0/2] PTRACE_SET_SYSCALL_INFO: add support for seccomp syscall skipping Renzo Davoli
2026-08-31 8:27 ` [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
@ 2026-08-31 8:27 ` Renzo Davoli
1 sibling, 0 replies; 5+ messages in thread
From: Renzo Davoli @ 2026-08-31 8:27 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, Thomas Bogendoerfer
Check whether PTRACE_SET_SYSCALL_INFO syscall skipping semantics implemented in the
kernel matches userspace expectations.
Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
Reviewed-by: Dmitry V. Levin <ldv@strace.io>
---
.../selftests/ptrace/set_syscall_info.c | 184 +++++++++++++++++-
1 file changed, 183 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..cb2fe5295ae3 100644
--- a/tools/testing/selftests/ptrace/set_syscall_info.c
+++ b/tools/testing/selftests/ptrace/set_syscall_info.c
@@ -11,9 +11,15 @@
#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 +42,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 +71,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 +154,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 +163,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 +542,160 @@ 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");
+ }
+ ASSERT_EQ(0, sys_seccomp(SECCOMP_SET_MODE_FILTER, 0,
+ (void *) &seccomp_prog)) {
+ TH_LOG("seccomp: %m");
+ }
+
+ /* run getpid unmodified */
+ ASSERT_EQ(tracee_pid, getpid()) {
+ _exit(1);
+ }
+
+ /* run getppid instead of getpid */
+ ASSERT_EQ(tracer_pid, getpid()) {
+ _exit(1);
+ }
+
+ /* skip getpid and return 42 */
+ ASSERT_EQ(42, getpid()) {
+ _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");
+ }
+
+ const char *testname[] = {
+ "unknown error",
+ "getpid seccomp unchanged",
+ "getpid seccomp nr change: getppid",
+ "getpid seccomp skip+set retvalue"
+ };
+
+ const unsigned int expected_nr[] = {
+ __NR_getpid,
+ __NR_getpid,
+ __NR_getpid,
+ __NR_exit_group
+ };
+
+ for (ptrace_stop = 0; ; ++ptrace_stop) {
+ 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);
+ }
+
+ 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");
+ }
+ 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");
+ }
+
+ ASSERT_LT(ptrace_stop, ARRAY_SIZE(expected_nr)) {
+ LOG_KILL_TRACEE("ptrace stop overflow");
+ }
+ ASSERT_FALSE(info.seccomp.nr == __NR_exit_group && info.seccomp.args[0] == 1) {
+ LOG_KILL_TRACEE("tracee error: %s",
+ testname[ptrace_stop]);
+ }
+ 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)) {
+ 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");
+ }
+ }
+}
+
TEST_HARNESS_MAIN
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support
2026-08-31 8:27 ` [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
@ 2026-08-31 13:20 ` Oleg Nesterov
2026-08-31 13:43 ` Michal Suchánek
0 siblings, 1 reply; 5+ messages in thread
From: Oleg Nesterov @ 2026-08-31 13:20 UTC (permalink / raw)
To: Renzo Davoli, Michal Suchánek
Cc: linux-kernel, Andrew Morton, Shuah Khan, Alexey Gladkov,
Eugene Syromyatnikov, Davide Berardi, strace-devel,
Dmitry V . Levin, Thomas Bogendoerfer
Well, still looks good to me...
But I forgot (sorry, actually didn't really read) the previous discussions
and IIRC Michal had some concerns about this patch. And you don't CC him.
Michal, can you take a look? Do you see anything wrong?
Oleg.
On 08/31, Renzo Davoli wrote:
>
> 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.
>
> System call suppression via PTRACE_SYSCALL_INFO_ENTRY is currently not
> implemented. On some architectures (e.g. MIPS), when a system call is
> skipped by setting the syscall number to -1 at the entry stop, the
> architecture entry path unconditionally overwrites the return value
> register with -ENOSYS, clobbering any custom return value set by the
> tracer at the entry stop.
>
> Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
> Reviewed-by: Oleg Nesterov <oleg@redhat.com>
> Reviewed-by: Dmitry V. Levin <ldv@strace.io>
> ---
> kernel/ptrace.c | 27 ++++++++++++++++++++++-----
> 1 file changed, 22 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/ptrace.c b/kernel/ptrace.c
> index d041645d9d17..64fd1b455297 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 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
> + */
> +
> + op = ptrace_get_syscall_info_op(child);
> + if (op != info.op) {
> + if (info.op == PTRACE_SYSCALL_INFO_EXIT &&
> + 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.55.0
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support
2026-08-31 13:20 ` Oleg Nesterov
@ 2026-08-31 13:43 ` Michal Suchánek
0 siblings, 0 replies; 5+ messages in thread
From: Michal Suchánek @ 2026-08-31 13:43 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Renzo Davoli, linux-kernel, Andrew Morton, Shuah Khan,
Alexey Gladkov, Eugene Syromyatnikov, Davide Berardi,
strace-devel, Dmitry V . Levin, Thomas Bogendoerfer
Hello,
on some architectures the syscall return value overlaps with the syscall
number or syscall arguments - see
https://www.man7.org/linux/man-pages/man2/syscall.2.html
When calling a seccomp filter the filter is able to return a value
which then tells the kernel if the syscall state should be interpreted
as entry (syscall nr + arguments) or exit (syscall skipped, registers
have return value).
ptrace has no way to return a value, and from the register state alone
it is not possible to tell if the syscall state is entry or exit.
Some architectures affected by this overlap problem implement an
architecture-specific thread info flag that tells the kernel that the
syscall is alreaady dispatched. However, this flag is
architecture-specific, and you would need to set it in architecture
independent code.
You could make this flag architecture independent (and even set it to 0
on architectures that are not affected) which would then allow this to
work in general.
Otherwise this API is non-protable, and broken from the start.
On Mon, Aug 31, 2026 at 03:20:15PM +0200, Oleg Nesterov wrote:
> Well, still looks good to me...
>
> But I forgot (sorry, actually didn't really read) the previous discussions
> and IIRC Michal had some concerns about this patch. And you don't CC him.
>
> Michal, can you take a look? Do you see anything wrong?
>
> Oleg.
>
> On 08/31, Renzo Davoli wrote:
> >
> > 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.
> >
> > System call suppression via PTRACE_SYSCALL_INFO_ENTRY is currently not
> > implemented. On some architectures (e.g. MIPS), when a system call is
> > skipped by setting the syscall number to -1 at the entry stop, the
> > architecture entry path unconditionally overwrites the return value
> > register with -ENOSYS, clobbering any custom return value set by the
> > tracer at the entry stop.
> >
> > Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
> > Reviewed-by: Oleg Nesterov <oleg@redhat.com>
> > Reviewed-by: Dmitry V. Levin <ldv@strace.io>
> > ---
> > kernel/ptrace.c | 27 ++++++++++++++++++++++-----
> > 1 file changed, 22 insertions(+), 5 deletions(-)
> >
> > diff --git a/kernel/ptrace.c b/kernel/ptrace.c
> > index d041645d9d17..64fd1b455297 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 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
> > + */
> > +
> > + op = ptrace_get_syscall_info_op(child);
> > + if (op != info.op) {
> > + if (info.op == PTRACE_SYSCALL_INFO_EXIT &&
> > + 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);
Here it might be preferable to define a new function that does the
skip=true part and calls ptrace_set_syscall_info_exit.
Looking at the code it would not be the first.
Thanks
Michal
> > case PTRACE_SYSCALL_INFO_SECCOMP:
> > return ptrace_set_syscall_info_seccomp(child, regs, &info);
> > default:
> > --
> > 2.55.0
> >
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-31 13:44 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-31 8:27 [PATCH v7 0/2] PTRACE_SET_SYSCALL_INFO: add support for seccomp syscall skipping Renzo Davoli
2026-08-31 8:27 ` [PATCH v7 1/2] ptrace: add PTRACE_SET_SYSCALL_INFO syscall skipping support Renzo Davoli
2026-08-31 13:20 ` Oleg Nesterov
2026-08-31 13:43 ` Michal Suchánek
2026-08-31 8:27 ` [PATCH v7 2/2] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO syscall skipping Renzo Davoli
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®