MIPS: seccomp: Handle indirect system calls (o32)
authorMarkos Chandras <markos.chandras@imgtec.com>
Wed, 22 Jan 2014 14:40:03 +0000 (14:40 +0000)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 26 Mar 2014 22:09:08 +0000 (23:09 +0100)
When userland uses syscall() to perform an indirect system call
the actually system call that needs to be checked by the filter
is on the first argument. The kernel code needs to handle this case
by looking at the original syscall number in v0 and if it's
NR_syscall, then it needs to examine the first argument to
identify the real system call that will be executed.
Similarly, we need to 'virtually' shift the syscall() arguments
so the syscall_get_arguments() function can fetch the correct
arguments for the indirect system call.

Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Reviewed-by: James Hogan <james.hogan@imgtec.com>
Reviewed-by: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/6404/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/ptrace.h
arch/mips/include/asm/syscall.h
arch/mips/kernel/ptrace.c
arch/mips/kernel/scall32-o32.S
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/scall64-o32.S

index 84257dfa3b600125e0d6922b792de30a51c84c38..bf1ac8d3578387c19a98b5cd8177c05fa4a1b309 100644 (file)
@@ -82,7 +82,7 @@ static inline long regs_return_value(struct pt_regs *regs)
 #define instruction_pointer(regs) ((regs)->cp0_epc)
 #define profile_pc(regs) instruction_pointer(regs)
 
-extern asmlinkage long syscall_trace_enter(struct pt_regs *regs);
+extern asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall);
 extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
 
 extern void die(const char *, struct pt_regs *) __noreturn;
index 08b639b1ca780a5b82c8763c9cc211485e368226..9031745cec1bbb5bfed5caa9012afb5ffcdcf64d 100644 (file)
 #include <linux/sched.h>
 #include <linux/uaccess.h>
 #include <asm/ptrace.h>
+#include <asm/unistd.h>
+
+#ifndef __NR_syscall /* Only defined if _MIPS_SIM == _MIPS_SIM_ABI32 */
+#define __NR_syscall 4000
+#endif
 
 static inline long syscall_get_nr(struct task_struct *task,
                                  struct pt_regs *regs)
 {
-       return regs->regs[2];
+       /* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */
+       if ((config_enabled(CONFIG_32BIT) ||
+           test_tsk_thread_flag(task, TIF_32BIT_REGS)) &&
+           (regs->regs[2] == __NR_syscall))
+               return regs->regs[4];
+       else
+               return regs->regs[2];
 }
 
 static inline unsigned long mips_get_syscall_arg(unsigned long *arg,
@@ -91,6 +102,13 @@ static inline void syscall_get_arguments(struct task_struct *task,
 {
        unsigned long arg;
        int ret;
+       /* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */
+       if ((config_enabled(CONFIG_32BIT) ||
+           test_tsk_thread_flag(task, TIF_32BIT_REGS)) &&
+           (regs->regs[2] == __NR_syscall)) {
+               i++;
+               n++;
+       }
 
        while (n--)
                ret |= mips_get_syscall_arg(&arg, task, regs, i++);
index 7f9bcaac467e6639e4106831344af3dde7c1de37..a17a7023d7c98d985f8a40f61ad8b6252ce7fae4 100644 (file)
@@ -662,9 +662,8 @@ long arch_ptrace(struct task_struct *child, long request,
  * Notification of system call entry/exit
  * - triggered by current->work.syscall_trace
  */
-asmlinkage long syscall_trace_enter(struct pt_regs *regs)
+asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall)
 {
-       long syscall = regs->regs[2];
        long ret = 0;
        user_exit();
 
index 1789a801802ee2c5a1e992f6fcaecbb98840a1fa..ffe89139e0f92ef10d80d74985ace86476594610 100644 (file)
@@ -118,7 +118,16 @@ syscall_trace_entry:
        SAVE_STATIC
        move    s0, t2
        move    a0, sp
-       jal     syscall_trace_enter
+
+       /*
+        * syscall number is in v0 unless we called syscall(__NR_###)
+        * where the real syscall number is in a0
+        */
+       addiu   a1, v0,  __NR_O32_Linux
+       bnez    v0, 1f /* __NR_syscall at offset 0 */
+       lw      a1, PT_R4(sp)
+
+1:     jal     syscall_trace_enter
 
        bltz    v0, 2f                  # seccomp failed? Skip syscall
 
index 7f5d88be6b9e0fc9f9dda616c115a297ff5bd75f..dd99c3285aeae75f65ae982de46a755ae953f140 100644 (file)
@@ -80,6 +80,7 @@ syscall_trace_entry:
        SAVE_STATIC
        move    s0, t2
        move    a0, sp
+       daddiu  a1, v0, __NR_64_Linux
        jal     syscall_trace_enter
 
        bltz    v0, 2f                  # seccomp failed? Skip syscall
index b6e15861bd1ba4bcc6b5f6318f7476a5f8c63dec..f68d2f4f009021de3ed784e9733aee5d3580d8e0 100644 (file)
@@ -72,6 +72,7 @@ n32_syscall_trace_entry:
        SAVE_STATIC
        move    s0, t2
        move    a0, sp
+       daddiu  a1, v0, __NR_N32_Linux
        jal     syscall_trace_enter
 
        bltz    v0, 2f                  # seccomp failed? Skip syscall
index 67dc022f682602447908db4498b6b204ffc2f7b3..70f6acecd928896c54b586b3470c27eb703721be 100644 (file)
@@ -112,7 +112,18 @@ trace_a_syscall:
 
        move    s0, t2                  # Save syscall pointer
        move    a0, sp
-       jal     syscall_trace_enter
+       /*
+        * syscall number is in v0 unless we called syscall(__NR_###)
+        * where the real syscall number is in a0
+        * note: NR_syscall is the first O32 syscall but the macro is
+        * only defined when compiling with -mabi=32 (CONFIG_32BIT)
+        * therefore __NR_O32_Linux is used (4000)
+        */
+       addiu   a1, v0,  __NR_O32_Linux
+       bnez    v0, 1f /* __NR_syscall at offset 0 */
+       lw      a1, PT_R4(sp)
+
+1:     jal     syscall_trace_enter
 
        bltz    v0, 2f                  # seccomp failed? Skip syscall