On 18/10/26 8:45 pm, Renzo Davoli wrote:
From: Davide Berardi <[email protected]>

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 <[email protected]>
Cc: Mukesh Kumar Chaurasiya (IBM) <[email protected]>
Cc: Shrikanth Hegde <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Assisted-by: claude-opus-5-5
Signed-off-by: Renzo Davoli <[email protected]>
---
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

Reply via email to