mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] powerpc/syscall: Return the preset value of skipped syscalls
@ 2026-10-18 15:15 Renzo Davoli
  2026-10-21  7:17 ` Mukesh Kumar Chaurasiya
  2026-10-23  8:58 ` samir
  0 siblings, 2 replies; 4+ messages in thread
From: Renzo Davoli @ 2026-10-18 15:15 UTC (permalink / raw)
  To: Madhavan Srinivasan, Michael Ellerman
  Cc: Davide Berardi, Nicholas Piggin, Christophe Leroy,
	Michal Suchánek, Mukesh Kumar Chaurasiya, Shrikanth Hegde,
	Thomas Gleixner, linuxppc-dev, linux-kernel, Renzo Davoli,
	Oleg Nesterov, Dmitry V . Levin, Thomas Bogendoerfer,
	Andrew Morton

From: Davide Berardi <berardi.dav@gmail.com>

When ptrace or seccomp skip a system call, the return value they set in
regs->gpr[3] gets lost or the tracer is affected, since two recent
changes:

1. Commit 05c033db7e9a ("entry, treewide: Make
   syscall_enter_from_user_mode[_work]() indicate syscall execution")
   made system_call_exception() return syscall_get_error() for a skipped
   system call.  syscall_get_error() is 0 for any non-error value, so a
   success value set by a tracer at a PTRACE_EVENT_SECCOMP stop (e.g.
   with PTRACE_POKEUSER), or by a SECCOMP_RET_USER_NOTIF listener, is
   replaced with 0.

2. Commit 69cb2be898be ("powerpc/syscall: Fix syscall skip handling for
   seccomp and ptrace") made syscall_set_return_value() set
   TIF_SYSCALL_RET with set_thread_flag(), i.e. on current rather than on
   the task passed in.  When the caller is a tracer, as in
   PTRACE_SET_SYSCALL_INFO with op == PTRACE_SYSCALL_INFO_EXIT, the flag
   ends up on the tracer, and the next system call of the tracer is
   silently not executed.  Setting it on the right task would not be
   enough either: at a syscall-exit stop the flag would then skip the
   next system call of the tracee.

TIF_SYSCALL_RET is not needed anymore: after the change described in 1.
the generic entry code tells whether ptrace or seccomp skipped the
system call, and every in-kernel caller of syscall_set_return_value() on
the syscall entry path (SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE without a
tracer, SECCOMP_RET_USER_NOTIF) also makes it return false.  A user
calling syscall(-1) without tracer or seccomp still gets -ENOSYS.

Remove the flag and return regs->gpr[3] for a skipped system call, as
done before the conversion to GENERIC_ENTRY.  The syscall exit path
already converts a negative error value to the "sc" ABI, and
syscall_set_return_value() keeps CR0.SO in sync for positive error
values.

Fixes: 69cb2be898be ("powerpc/syscall: Fix syscall skip handling for seccomp and ptrace")
Fixes: 05c033db7e9a ("entry, treewide: Make syscall_enter_from_user_mode[_work]() indicate syscall execution")
Cc: Michal Suchánek <msuchanek@suse.de>
Cc: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
Cc: Shrikanth Hegde <sshegde@linux.ibm.com>
Cc: Thomas Gleixner <tglx@kernel.org>
Assisted-by: claude-opus-5-5
Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
---
How this was found and tested: QEMU pseries (TCG, -cpu power9), ppc64le
guest config, v7.3-rc6 (7b63ef2d55f2), with a small reproducer
(ppc_skip_repro.c, available on request) which

 - sets the getpid() return value at a syscall-exit stop with
   PTRACE_SET_SYSCALL_INFO, then makes a syscall in the tracer;
 - skips getpid() at a PTRACE_EVENT_SECCOMP stop with PTRACE_POKEUSER
   (r0 = -1, r3/CR0.SO = return value), with both "sc" and "scv 0".

                                             v7.3-rc6      with this patch
  tracer syscall after SET_SYSCALL_INFO      returns 0     correct
  skipped syscall, success value, sc/scv     returns 0     correct
  skipped syscall, error value, sc/scv       correct       correct
  selftests/ptrace set_syscall_info test    FAIL          pass
  selftests/seccomp/seccomp_bpf              11 failures   all pass (*)

  failing in v7.3-rc6: user_notification_{basic,child_pid_ns,
  sibling_pid_ns,fault_recv,addfd,addfd_rlimit,sync,fifo,wait_killable},
  TRACE_syscall.seccomp.syscall_faked, plus the timing-only one below.

(*) user_notification_wait_killable_after_reply (100000 notifications
    with a 1 ms itimer) can hit the 30 s harness timeout under emulation,
    with and without this patch.

Build-tested with W=1: ppc64le guest config, ppc64_defconfig (BE),
pmac32_defconfig.

 arch/powerpc/include/asm/syscall.h     |  6 ------
 arch/powerpc/include/asm/thread_info.h |  1 -
 arch/powerpc/kernel/syscall.c          | 15 ++++++++-------
 3 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 32be22b995cc..4b3c52ed6e9d 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -98,12 +98,6 @@ static inline void syscall_set_return_value(struct task_struct *task,
 			regs->gpr[3] = val;
 		}
 	}
-	/*
-	 * Mark that a return value has been explicitly set by seccomp or
-	 * ptrace so that system_call_exception() can skip the syscall
-	 * unconditionally, even when the user requested syscall(-1).
-	 */
-	set_thread_flag(TIF_SYSCALL_RET);
 }
 
 static inline void syscall_get_arguments(struct task_struct *task,
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index 1e069a2e7ce8..0487e94d3416 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -120,7 +120,6 @@ void arch_setup_new_exec(void);
 #endif
 #define TIF_POLLING_NRFLAG	19	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_32BIT		20	/* 32 bit binary */
-#define TIF_SYSCALL_RET		21	/* syscall error value set */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
index fbefe1927b10..52ceff4ef41b 100644
--- a/arch/powerpc/kernel/syscall.c
+++ b/arch/powerpc/kernel/syscall.c
@@ -18,13 +18,14 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
 	long ret;
 	syscall_fn f;
 
-	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))) {
-		clear_thread_flag(TIF_SYSCALL_RET);
-		return syscall_get_error(current, regs);
-	}
-
-	if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET)))
-		return syscall_get_error(current, regs);
+	/*
+	 * If ptrace or seccomp decided to skip the syscall, regs->gpr[3] holds
+	 * the return value they set (or the unmodified first argument, e.g.
+	 * for PTRACE_SYSEMU). Return it as is, the exit path takes care of
+	 * the error encoding for the "sc" ABI.
+	 */
+	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0)))
+		return regs->gpr[3];
 
 	if (unlikely(r0 >= NR_syscalls)) {
 		if (unlikely(trap_is_unsupported_scv(regs))) {
-- 
2.55.0


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

* Re: [PATCH] powerpc/syscall: Return the preset value of skipped syscalls
  2026-10-18 15:15 [PATCH] powerpc/syscall: Return the preset value of skipped syscalls Renzo Davoli
@ 2026-10-21  7:17 ` Mukesh Kumar Chaurasiya
  2026-10-22 16:31   ` Renzo Davoli
  2026-10-23  8:58 ` samir
  1 sibling, 1 reply; 4+ messages in thread
From: Mukesh Kumar Chaurasiya @ 2026-10-21  7:17 UTC (permalink / raw)
  To: Renzo Davoli
  Cc: Madhavan Srinivasan, Michael Ellerman, Davide Berardi,
	Nicholas Piggin, Christophe Leroy, Michal Suchánek,
	Shrikanth Hegde, Thomas Gleixner, linuxppc-dev, linux-kernel,
	Oleg Nesterov, Dmitry V . Levin, Thomas Bogendoerfer,
	Andrew Morton

On Sun, Oct 18, 2026 at 05:15:33PM +0200, Renzo Davoli wrote:
> From: Davide Berardi <berardi.dav@gmail.com>
> 
> When ptrace or seccomp skip a system call, the return value they set in
> regs->gpr[3] gets lost or the tracer is affected, since two recent
> changes:
> 
> 1. Commit 05c033db7e9a ("entry, treewide: Make
>    syscall_enter_from_user_mode[_work]() indicate syscall execution")
>    made system_call_exception() return syscall_get_error() for a skipped
>    system call.  syscall_get_error() is 0 for any non-error value, so a
>    success value set by a tracer at a PTRACE_EVENT_SECCOMP stop (e.g.
>    with PTRACE_POKEUSER), or by a SECCOMP_RET_USER_NOTIF listener, is
>    replaced with 0.
> 
> 2. Commit 69cb2be898be ("powerpc/syscall: Fix syscall skip handling for
>    seccomp and ptrace") made syscall_set_return_value() set
>    TIF_SYSCALL_RET with set_thread_flag(), i.e. on current rather than on
>    the task passed in.  When the caller is a tracer, as in
>    PTRACE_SET_SYSCALL_INFO with op == PTRACE_SYSCALL_INFO_EXIT, the flag
>    ends up on the tracer, and the next system call of the tracer is
>    silently not executed.  Setting it on the right task would not be
>    enough either: at a syscall-exit stop the flag would then skip the
>    next system call of the tracee.
> 
> TIF_SYSCALL_RET is not needed anymore: after the change described in 1.
> the generic entry code tells whether ptrace or seccomp skipped the
> system call, and every in-kernel caller of syscall_set_return_value() on
> the syscall entry path (SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE without a
> tracer, SECCOMP_RET_USER_NOTIF) also makes it return false.  A user
> calling syscall(-1) without tracer or seccomp still gets -ENOSYS.
> 
> Remove the flag and return regs->gpr[3] for a skipped system call, as
> done before the conversion to GENERIC_ENTRY.  The syscall exit path
> already converts a negative error value to the "sc" ABI, and
> syscall_set_return_value() keeps CR0.SO in sync for positive error
> values.
> 
> Fixes: 69cb2be898be ("powerpc/syscall: Fix syscall skip handling for seccomp and ptrace")
> Fixes: 05c033db7e9a ("entry, treewide: Make syscall_enter_from_user_mode[_work]() indicate syscall execution")
> Cc: Michal Suchánek <msuchanek@suse.de>
> Cc: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> Cc: Shrikanth Hegde <sshegde@linux.ibm.com>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Assisted-by: claude-opus-5-5
> Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
> ---
> How this was found and tested: QEMU pseries (TCG, -cpu power9), ppc64le
> guest config, v7.3-rc6 (7b63ef2d55f2), with a small reproducer
> (ppc_skip_repro.c, available on request) which
> 
Hey,
Can you share the reproducer?

Regards,
Mukesh
>  - sets the getpid() return value at a syscall-exit stop with
>    PTRACE_SET_SYSCALL_INFO, then makes a syscall in the tracer;
>  - skips getpid() at a PTRACE_EVENT_SECCOMP stop with PTRACE_POKEUSER
>    (r0 = -1, r3/CR0.SO = return value), with both "sc" and "scv 0".
> 
>                                              v7.3-rc6      with this patch
>   tracer syscall after SET_SYSCALL_INFO      returns 0     correct
>   skipped syscall, success value, sc/scv     returns 0     correct
>   skipped syscall, error value, sc/scv       correct       correct
>   selftests/ptrace set_syscall_info test    FAIL          pass
>   selftests/seccomp/seccomp_bpf              11 failures   all pass (*)
> 
>   failing in v7.3-rc6: user_notification_{basic,child_pid_ns,
>   sibling_pid_ns,fault_recv,addfd,addfd_rlimit,sync,fifo,wait_killable},
>   TRACE_syscall.seccomp.syscall_faked, plus the timing-only one below.
> 
> (*) user_notification_wait_killable_after_reply (100000 notifications
>     with a 1 ms itimer) can hit the 30 s harness timeout under emulation,
>     with and without this patch.
> 
> Build-tested with W=1: ppc64le guest config, ppc64_defconfig (BE),
> pmac32_defconfig.
> 
>  arch/powerpc/include/asm/syscall.h     |  6 ------
>  arch/powerpc/include/asm/thread_info.h |  1 -
>  arch/powerpc/kernel/syscall.c          | 15 ++++++++-------
>  3 files changed, 8 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
> index 32be22b995cc..4b3c52ed6e9d 100644
> --- a/arch/powerpc/include/asm/syscall.h
> +++ b/arch/powerpc/include/asm/syscall.h
> @@ -98,12 +98,6 @@ static inline void syscall_set_return_value(struct task_struct *task,
>  			regs->gpr[3] = val;
>  		}
>  	}
> -	/*
> -	 * Mark that a return value has been explicitly set by seccomp or
> -	 * ptrace so that system_call_exception() can skip the syscall
> -	 * unconditionally, even when the user requested syscall(-1).
> -	 */
> -	set_thread_flag(TIF_SYSCALL_RET);
>  }
>  
>  static inline void syscall_get_arguments(struct task_struct *task,
> diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
> index 1e069a2e7ce8..0487e94d3416 100644
> --- a/arch/powerpc/include/asm/thread_info.h
> +++ b/arch/powerpc/include/asm/thread_info.h
> @@ -120,7 +120,6 @@ void arch_setup_new_exec(void);
>  #endif
>  #define TIF_POLLING_NRFLAG	19	/* true if poll_idle() is polling TIF_NEED_RESCHED */
>  #define TIF_32BIT		20	/* 32 bit binary */
> -#define TIF_SYSCALL_RET		21	/* syscall error value set */
>  
>  /* as above, but as bit values */
>  #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
> diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
> index fbefe1927b10..52ceff4ef41b 100644
> --- a/arch/powerpc/kernel/syscall.c
> +++ b/arch/powerpc/kernel/syscall.c
> @@ -18,13 +18,14 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
>  	long ret;
>  	syscall_fn f;
>  
> -	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))) {
> -		clear_thread_flag(TIF_SYSCALL_RET);
> -		return syscall_get_error(current, regs);
> -	}
> -
> -	if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET)))
> -		return syscall_get_error(current, regs);
> +	/*
> +	 * If ptrace or seccomp decided to skip the syscall, regs->gpr[3] holds
> +	 * the return value they set (or the unmodified first argument, e.g.
> +	 * for PTRACE_SYSEMU). Return it as is, the exit path takes care of
> +	 * the error encoding for the "sc" ABI.
> +	 */
> +	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0)))
> +		return regs->gpr[3];
>  
>  	if (unlikely(r0 >= NR_syscalls)) {
>  		if (unlikely(trap_is_unsupported_scv(regs))) {
> -- 
> 2.55.0
> 

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

* Re: [PATCH] powerpc/syscall: Return the preset value of skipped syscalls
  2026-10-21  7:17 ` Mukesh Kumar Chaurasiya
@ 2026-10-22 16:31   ` Renzo Davoli
  0 siblings, 0 replies; 4+ messages in thread
From: Renzo Davoli @ 2026-10-22 16:31 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya
  Cc: Madhavan Srinivasan, Michael Ellerman, Davide Berardi,
	Nicholas Piggin, Christophe Leroy, Michal Suchánek,
	Shrikanth Hegde, Thomas Gleixner, linuxppc-dev, linux-kernel,
	Oleg Nesterov, Dmitry V . Levin, Thomas Bogendoerfer,
	Andrew Morton

[-- Attachment #1: Type: text/plain, Size: 1475 bytes --]

On Wed, Oct 21, 2026 at 12:47:36PM +0530, Mukesh Kumar Chaurasiya wrote:
> > How this was found and tested: QEMU pseries (TCG, -cpu power9), ppc64le
> > guest config, v7.3-rc6 (7b63ef2d55f2), with a small reproducer
> > (ppc_skip_repro.c, available on request) which
> > 
> Hey,
> Can you share the reproducer?
> 
> Regards,
> Mukesh

sure, it is here attached.

The output here follows.

before patch:
tracer syscall after PTRACE_SET_SYSCALL_INFO(EXIT)         FAIL (got 0, want 1)
tracee sees value set at syscall-exit stop                 PASS (got 0, want 0)
    tracee got 0
seccomp skip via regs, success value, sc                   FAIL (got 1, want 0)
seccomp skip via regs, error value,   sc                   PASS (got 0, want 0)
    tracee got 0
seccomp skip via regs, success value, scv                  FAIL (got 1, want 0)
seccomp skip via regs, error value,   scv                  PASS (got 0, want 0)
FAIL: 3 failure(s)

after patch:
tracer syscall after PTRACE_SET_SYSCALL_INFO(EXIT)         PASS (got 1, want 1)
tracee sees value set at syscall-exit stop                 PASS (got 0, want 0)
seccomp skip via regs, success value, sc                   PASS (got 0, want 0)
seccomp skip via regs, error value,   sc                   PASS (got 0, want 0)
seccomp skip via regs, success value, scv                  PASS (got 0, want 0)
seccomp skip via regs, error value,   scv                  PASS (got 0, want 0)
OK: 0 failure(s)

Thank you, Regards,
	renzo

[-- Attachment #2: ppc_skip_repro.c --]
[-- Type: text/x-csrc, Size: 7467 bytes --]

// SPDX-License-Identifier: GPL-2.0
/*
 * Reproducer for two powerpc syscall-skip problems introduced with
 * GENERIC_ENTRY / TIF_SYSCALL_RET (commit 69cb2be898be):
 *
 *  1. syscall_set_return_value() sets TIF_SYSCALL_RET on current, so when a
 *     tracer uses PTRACE_SET_SYSCALL_INFO (op == EXIT) the flag lands on the
 *     *tracer*, whose next system call is silently skipped.
 *
 *  2. When a syscall is skipped, system_call_exception() returns
 *     syscall_get_error(), so a non-error return value set by a tracer
 *     (or by SECCOMP_RET_USER_NOTIF) is replaced by 0.
 *
 * Test 2 is run with both the "sc" and the "scv 0" system call instructions
 * on powerpc.  On x86_64 the same tests are run with "syscall" as a control.
 *
 * Build: $CC -static -O2 -o ppc_skip_repro ppc_skip_repro.c
 */
#define _GNU_SOURCE
#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <linux/filter.h>
#include <linux/seccomp.h>

#ifndef PTRACE_GET_SYSCALL_INFO
# define PTRACE_GET_SYSCALL_INFO 0x420e
#endif
#ifndef PTRACE_SET_SYSCALL_INFO
# define PTRACE_SET_SYSCALL_INFO 0x4212
#endif
#define PSI_ENTRY	1
#define PSI_EXIT	2

struct psi {
	unsigned char op, reserved;
	unsigned short flags;
	unsigned int arch;
	unsigned long long ip, sp;
	union {
		struct { unsigned long long nr, args[6]; } entry;
		struct { long long rval; unsigned char is_error; } exit;
		struct { unsigned long long nr, args[6]; unsigned int ret_data, reserved2; } seccomp;
	};
};

#define MAGIC 42

#if defined(__powerpc64__)
# define PT_R0		0
# define PT_R3		3
# define PT_CCR		38
# define PT_TRAP	40
# define CR0_SO		0x10000000UL
# ifndef PPC_FEATURE2_SCV
#  define PPC_FEATURE2_SCV 0x00100000
# endif

/* getpid via "sc": error is reported in CR0.SO with a positive errno */
static long sys_getpid_sc(void)
{
	register long r0 __asm__("r0") = __NR_getpid;
	register long r3 __asm__("r3");
	unsigned long cr;

	__asm__ volatile(".long 0x44000002\n\tmfcr %2"		/* sc */
			 : "+r"(r0), "=r"(r3), "=r"(cr)
			 :
			 : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11",
			   "r12", "ctr", "xer", "cr0", "memory");
	return (cr & CR0_SO) ? -r3 : r3;
}

/* getpid via "scv 0": error is reported as a negative value */
static long sys_getpid_scv(void)
{
	register long r0 __asm__("r0") = __NR_getpid;
	register long r3 __asm__("r3");

	__asm__ volatile(".long 0x44000001"			/* scv 0 */
			 : "+r"(r0), "=r"(r3)
			 :
			 : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11",
			   "r12", "lr", "ctr", "xer", "cr0", "cr1", "cr5",
			   "cr6", "cr7", "memory");
	return r3;
}

/* Skip the syscall the old-fashioned way: r0 = -1, r3/ccr = return value */
static int poke_skip(pid_t pid, long val)
{
	long trap = ptrace(PTRACE_PEEKUSER, pid, PT_TRAP * 8, 0);
	long ccr = ptrace(PTRACE_PEEKUSER, pid, PT_CCR * 8, 0);

	if ((trap & 0xfff0) != 0x3000)	/* "sc": positive errno + CR0.SO */
		ccr = val < 0 ? (ccr | CR0_SO) : (ccr & ~CR0_SO);
	if ((trap & 0xfff0) != 0x3000 && val < 0)
		val = -val;
	return ptrace(PTRACE_POKEUSER, pid, PT_R0 * 8, -1L) ||
	       ptrace(PTRACE_POKEUSER, pid, PT_R3 * 8, val) ||
	       ptrace(PTRACE_POKEUSER, pid, PT_CCR * 8, ccr);
}
#elif defined(__x86_64__)
static long sys_getpid_sc(void)
{
	long ret;

	__asm__ volatile("syscall" : "=a"(ret) : "a"((long)__NR_getpid)
			 : "rcx", "r11", "memory");
	return ret;
}

static int poke_skip(pid_t pid, long val)
{
	return ptrace(PTRACE_POKEUSER, pid, 15 * 8 /* orig_rax */, -1L) ||
	       ptrace(PTRACE_POKEUSER, pid, 10 * 8 /* rax */, val);
}
#else
# error unsupported architecture
#endif

static int failures;

static void report(const char *name, long got, long want)
{
	printf("%-58s %s (got %ld, want %ld)\n", name,
	       got == want ? "PASS" : "FAIL", got, want);
	if (got != want)
		failures++;
}

static pid_t start_tracee(void)
{
	pid_t pid = fork();

	if (pid == 0) {
		if (ptrace(PTRACE_TRACEME, 0, 0, 0))
			_exit(90);
		raise(SIGSTOP);
	}
	return pid;
}

static int wait_stop(pid_t pid)
{
	int status;

	if (waitpid(pid, &status, 0) != pid || !WIFSTOPPED(status))
		return -1;
	return status >> 8;
}

static long child_result(pid_t pid)
{
	int status;

	if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status))
		return -1000;
	return WEXITSTATUS(status);
}

/*
 * Test 1: the tracer sets the getpid() return value at the syscall-exit
 * stop with PTRACE_SET_SYSCALL_INFO, then makes a system call of its own.
 */
static void test_tracer_flag(void)
{
	pid_t me = getpid(), pid = start_tracee();
	struct psi info;
	long mine;

	if (pid == 0)
		_exit(syscall(__NR_getpid) == MAGIC ? 0 : 1);

	wait_stop(pid);
	ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);
	for (;;) {
		ptrace(PTRACE_SYSCALL, pid, 0, 0);
		if (wait_stop(pid) != (SIGTRAP | 0x80))
			break;
		memset(&info, 0, sizeof(info));
		ptrace(PTRACE_GET_SYSCALL_INFO, pid, sizeof(info), &info);
		if (info.op == PSI_EXIT)
			break;
	}
	info.exit.rval = MAGIC;
	info.exit.is_error = 0;
	if (ptrace(PTRACE_SET_SYSCALL_INFO, pid, sizeof(info), &info))
		perror("PTRACE_SET_SYSCALL_INFO");

	/* The very next syscall of the tracer must not be affected. */
	mine = sys_getpid_sc();
	report("tracer syscall after PTRACE_SET_SYSCALL_INFO(EXIT)", mine, me);

	ptrace(PTRACE_CONT, pid, 0, 0);
	report("tracee sees value set at syscall-exit stop", child_result(pid), 0);
}

/*
 * Test 2: seccomp RET_TRACE on getpid; at the seccomp stop the tracer skips
 * the syscall by setting the number to -1 and the return value with
 * PTRACE_POKEUSER (what existing ptrace users do).
 */
static void test_seccomp_skip(const char *name, long (*do_getpid)(void), long val)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_getpid, 0, 1),
		BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
		BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
	};
	struct sock_fprog prog = { .len = 4, .filter = filter };
	pid_t pid = start_tracee();

	if (pid == 0) {
		long ret;

		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
		    syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog))
			_exit(91);
		ret = do_getpid();
		/* exit status: 0 if correct, 1 otherwise; print what we got */
		if (ret != val)
			fprintf(stderr, "    tracee got %ld\n", ret);
		_exit(ret == val ? 0 : 1);
	}

	wait_stop(pid);
	ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESECCOMP);
	ptrace(PTRACE_CONT, pid, 0, 0);
	if (wait_stop(pid) != (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
		report(name, -1, 0);
		return;
	}
	if (poke_skip(pid, val))
		perror("poke");
	ptrace(PTRACE_CONT, pid, 0, 0);
	report(name, child_result(pid), 0);
}

int main(void)
{
	setvbuf(stdout, NULL, _IONBF, 0);
	test_tracer_flag();
	test_seccomp_skip("seccomp skip via regs, success value, sc", sys_getpid_sc, MAGIC);
	test_seccomp_skip("seccomp skip via regs, error value,   sc", sys_getpid_sc, -ENOENT);
#ifdef __powerpc64__
	if (getauxval(AT_HWCAP2) & PPC_FEATURE2_SCV) {
		test_seccomp_skip("seccomp skip via regs, success value, scv", sys_getpid_scv, MAGIC);
		test_seccomp_skip("seccomp skip via regs, error value,   scv", sys_getpid_scv, -ENOENT);
	} else {
		printf("scv not available, skipping scv tests\n");
	}
#endif
	printf("%s: %d failure(s)\n", failures ? "FAIL" : "OK", failures);
	return !!failures;
}

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

* Re: [PATCH] powerpc/syscall: Return the preset value of skipped syscalls
  2026-10-18 15:15 [PATCH] powerpc/syscall: Return the preset value of skipped syscalls Renzo Davoli
  2026-10-21  7:17 ` Mukesh Kumar Chaurasiya
@ 2026-10-23  8:58 ` samir
  1 sibling, 0 replies; 4+ messages in thread
From: samir @ 2026-10-23  8:58 UTC (permalink / raw)
  To: Renzo Davoli, Madhavan Srinivasan, Michael Ellerman
  Cc: Davide Berardi, Nicholas Piggin, Christophe Leroy,
	Michal Suchánek, Mukesh Kumar Chaurasiya, Shrikanth Hegde,
	Thomas Gleixner, linuxppc-dev, linux-kernel, Oleg Nesterov,
	Dmitry V . Levin, Thomas Bogendoerfer, Andrew Morton


On 18/10/26 8:45 pm, Renzo Davoli wrote:
> From: Davide Berardi <berardi.dav@gmail.com>
>
> When ptrace or seccomp skip a system call, the return value they set in
> regs->gpr[3] gets lost or the tracer is affected, since two recent
> changes:
>
> 1. Commit 05c033db7e9a ("entry, treewide: Make
>     syscall_enter_from_user_mode[_work]() indicate syscall execution")
>     made system_call_exception() return syscall_get_error() for a skipped
>     system call.  syscall_get_error() is 0 for any non-error value, so a
>     success value set by a tracer at a PTRACE_EVENT_SECCOMP stop (e.g.
>     with PTRACE_POKEUSER), or by a SECCOMP_RET_USER_NOTIF listener, is
>     replaced with 0.
>
> 2. Commit 69cb2be898be ("powerpc/syscall: Fix syscall skip handling for
>     seccomp and ptrace") made syscall_set_return_value() set
>     TIF_SYSCALL_RET with set_thread_flag(), i.e. on current rather than on
>     the task passed in.  When the caller is a tracer, as in
>     PTRACE_SET_SYSCALL_INFO with op == PTRACE_SYSCALL_INFO_EXIT, the flag
>     ends up on the tracer, and the next system call of the tracer is
>     silently not executed.  Setting it on the right task would not be
>     enough either: at a syscall-exit stop the flag would then skip the
>     next system call of the tracee.
>
> TIF_SYSCALL_RET is not needed anymore: after the change described in 1.
> the generic entry code tells whether ptrace or seccomp skipped the
> system call, and every in-kernel caller of syscall_set_return_value() on
> the syscall entry path (SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE without a
> tracer, SECCOMP_RET_USER_NOTIF) also makes it return false.  A user
> calling syscall(-1) without tracer or seccomp still gets -ENOSYS.
>
> Remove the flag and return regs->gpr[3] for a skipped system call, as
> done before the conversion to GENERIC_ENTRY.  The syscall exit path
> already converts a negative error value to the "sc" ABI, and
> syscall_set_return_value() keeps CR0.SO in sync for positive error
> values.
>
> Fixes: 69cb2be898be ("powerpc/syscall: Fix syscall skip handling for seccomp and ptrace")
> Fixes: 05c033db7e9a ("entry, treewide: Make syscall_enter_from_user_mode[_work]() indicate syscall execution")
> Cc: Michal Suchánek <msuchanek@suse.de>
> Cc: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> Cc: Shrikanth Hegde <sshegde@linux.ibm.com>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Assisted-by: claude-opus-5-5
> Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
> ---
> How this was found and tested: QEMU pseries (TCG, -cpu power9), ppc64le
> guest config, v7.3-rc6 (7b63ef2d55f2), with a small reproducer
> (ppc_skip_repro.c, available on request) which
>
>   - sets the getpid() return value at a syscall-exit stop with
>     PTRACE_SET_SYSCALL_INFO, then makes a syscall in the tracer;
>   - skips getpid() at a PTRACE_EVENT_SECCOMP stop with PTRACE_POKEUSER
>     (r0 = -1, r3/CR0.SO = return value), with both "sc" and "scv 0".
>
>                                               v7.3-rc6      with this patch
>    tracer syscall after SET_SYSCALL_INFO      returns 0     correct
>    skipped syscall, success value, sc/scv     returns 0     correct
>    skipped syscall, error value, sc/scv       correct       correct
>    selftests/ptrace set_syscall_info test    FAIL          pass
>    selftests/seccomp/seccomp_bpf              11 failures   all pass (*)
>
>    failing in v7.3-rc6: user_notification_{basic,child_pid_ns,
>    sibling_pid_ns,fault_recv,addfd,addfd_rlimit,sync,fifo,wait_killable},
>    TRACE_syscall.seccomp.syscall_faked, plus the timing-only one below.
>
> (*) user_notification_wait_killable_after_reply (100000 notifications
>      with a 1 ms itimer) can hit the 30 s harness timeout under emulation,
>      with and without this patch.
>
> Build-tested with W=1: ppc64le guest config, ppc64_defconfig (BE),
> pmac32_defconfig.
>
>   arch/powerpc/include/asm/syscall.h     |  6 ------
>   arch/powerpc/include/asm/thread_info.h |  1 -
>   arch/powerpc/kernel/syscall.c          | 15 ++++++++-------
>   3 files changed, 8 insertions(+), 14 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
> index 32be22b995cc..4b3c52ed6e9d 100644
> --- a/arch/powerpc/include/asm/syscall.h
> +++ b/arch/powerpc/include/asm/syscall.h
> @@ -98,12 +98,6 @@ static inline void syscall_set_return_value(struct task_struct *task,
>   			regs->gpr[3] = val;
>   		}
>   	}
> -	/*
> -	 * Mark that a return value has been explicitly set by seccomp or
> -	 * ptrace so that system_call_exception() can skip the syscall
> -	 * unconditionally, even when the user requested syscall(-1).
> -	 */
> -	set_thread_flag(TIF_SYSCALL_RET);
>   }
>   
>   static inline void syscall_get_arguments(struct task_struct *task,
> diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
> index 1e069a2e7ce8..0487e94d3416 100644
> --- a/arch/powerpc/include/asm/thread_info.h
> +++ b/arch/powerpc/include/asm/thread_info.h
> @@ -120,7 +120,6 @@ void arch_setup_new_exec(void);
>   #endif
>   #define TIF_POLLING_NRFLAG	19	/* true if poll_idle() is polling TIF_NEED_RESCHED */
>   #define TIF_32BIT		20	/* 32 bit binary */
> -#define TIF_SYSCALL_RET		21	/* syscall error value set */
>   
>   /* as above, but as bit values */
>   #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
> diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
> index fbefe1927b10..52ceff4ef41b 100644
> --- a/arch/powerpc/kernel/syscall.c
> +++ b/arch/powerpc/kernel/syscall.c
> @@ -18,13 +18,14 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
>   	long ret;
>   	syscall_fn f;
>   
> -	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))) {
> -		clear_thread_flag(TIF_SYSCALL_RET);
> -		return syscall_get_error(current, regs);
> -	}
> -
> -	if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET)))
> -		return syscall_get_error(current, regs);
> +	/*
> +	 * If ptrace or seccomp decided to skip the syscall, regs->gpr[3] holds
> +	 * the return value they set (or the unmodified first argument, e.g.
> +	 * for PTRACE_SYSEMU). Return it as is, the exit path takes care of
> +	 * the error encoding for the "sc" ABI.
> +	 */
> +	if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0)))
> +		return regs->gpr[3];
>   
>   	if (unlikely(r0 >= NR_syscalls)) {
>   		if (unlikely(trap_is_unsupported_scv(regs))) {
Hi Renzo,


I tried applying the patches to the latest upstream kernel (master 
branch), but the patch fails to apply with the following errors:

error: patch failed: arch/powerpc/include/asm/syscall.h:98
error: arch/powerpc/include/asm/syscall.h: patch does not apply
error: patch failed: arch/powerpc/kernel/syscall.c:18
error: arch/powerpc/kernel/syscall.c: patch does not apply

The latest commit on the master branch:
commit 734bef548a96e8774d6eef5dfa2026c6edc4e80b
(HEAD -> master, origin/master, origin/HEAD)


Could you please take a look and suggest how to resolve these conflicts? 
Does the patch need to be rebased against the latest upstream kernel?

Thanks for your help.

Best regards,
Samir

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

end of thread, other threads:[~2026-10-23  9:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-18 15:15 [PATCH] powerpc/syscall: Return the preset value of skipped syscalls Renzo Davoli
2026-10-21  7:17 ` Mukesh Kumar Chaurasiya
2026-10-22 16:31   ` Renzo Davoli
2026-10-23  8:58 ` samir

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®