parisc: Fix ptrace syscall number modification [Linux 5.0]

parisc: Fix ptrace syscall number modification [Linux 5.0]

This Linux kernel change "parisc: Fix ptrace syscall number modification" is included in the Linux 5.0 release. This change is authored by Dmitry V. Levin <ldv [at] altlinux.org> on Sat Feb 16 16:10:39 2019 +0300. The commit for this change in Linux stable tree is b7dc5a0 (patch).

parisc: Fix ptrace syscall number modification

Commit 910cd32e552e ("parisc: Fix and enable seccomp filter support")
introduced a regression in ptrace-based syscall tampering: when tracer
changes syscall number to -1, the kernel fails to initialize %r28 with
-ENOSYS and subsequently fails to return the error code of the failed
syscall to userspace.

This erroneous behaviour could be observed with a simple strace syscall
fault injection command which is expected to print something like this:

$ strace -a0 -ewrite -einject=write:error=enospc echo hello
write(1, "hello\n", 6) = -1 ENOSPC (No space left on device) (INJECTED)
write(2, "echo: ", 6) = -1 ENOSPC (No space left on device) (INJECTED)
write(2, "write error", 11) = -1 ENOSPC (No space left on device) (INJECTED)
write(2, "\n", 1) = -1 ENOSPC (No space left on device) (INJECTED)
+++ exited with 1 +++

After commit 910cd32e552ea09caa89cdbe328e468979b030dd it loops printing
something like this instead:

write(1, "hello\n", 6../strace: Failed to tamper with process 12345: unexpectedly got no error (return value 0, error 0)
) = 0 (INJECTED)

This bug was found by strace test suite.

Fixes: 910cd32e552e ("parisc: Fix and enable seccomp filter support")
Cc: [email protected] # v4.5+
Signed-off-by: Dmitry V. Levin <[email protected]>
Tested-by: Helge Deller <[email protected]>
Signed-off-by: Helge Deller <[email protected]>

There are 29 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 arch/parisc/kernel/ptrace.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
index 2582df1..0964c23 100644
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -308,15 +308,29 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,

 long do_syscall_trace_enter(struct pt_regs *regs)
 {
-   if (test_thread_flag(TIF_SYSCALL_TRACE) &&
-       tracehook_report_syscall_entry(regs)) {
+   if (test_thread_flag(TIF_SYSCALL_TRACE)) {
+       int rc = tracehook_report_syscall_entry(regs);
+
        /*
-        * Tracing decided this syscall should not happen or the
-        * debugger stored an invalid system call number. Skip
-        * the system call and the system call restart handling.
+        * As tracesys_next does not set %r28 to -ENOSYS
+        * when %r20 is set to -1, initialize it here.
         */
-       regs->gr[20] = -1UL;
-       goto out;
+       regs->gr[28] = -ENOSYS;
+
+       if (rc) {
+           /*
+            * A nonzero return code from
+            * tracehook_report_syscall_entry() tells us
+            * to prevent the syscall execution.  Skip
+            * the syscall call and the syscall restart handling.
+            *
+            * Note that the tracer may also just change
+            * regs->gr[20] to an invalid syscall number,
+            * that is handled by tracesys_next.
+            */
+           regs->gr[20] = -1UL;
+           return -1;
+       }
    }

    /* Do the secure computing check after ptrace. */
@@ -340,7 +354,6 @@ long do_syscall_trace_enter(struct pt_regs *regs)
            regs->gr[24] & 0xffffffff,
            regs->gr[23] & 0xffffffff);

-out:
    /*
     * Sign extend the syscall number to 64bit since it may have been
     * modified by a compat ptrace call

Leave a Reply

Your email address will not be published. Required fields are marked *