From 4c21b8fd8f146a22e1eaf92833a32e51f560e82a Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Wed, 22 Jan 2014 14:40:03 +0000 Subject: MIPS: seccomp: Handle indirect system calls (o32) 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 Reviewed-by: James Hogan Reviewed-by: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/6404/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/ptrace.h | 2 +- arch/mips/include/asm/syscall.h | 20 +++++++++++++++++++- arch/mips/kernel/ptrace.c | 3 +-- arch/mips/kernel/scall32-o32.S | 11 ++++++++++- arch/mips/kernel/scall64-64.S | 1 + arch/mips/kernel/scall64-n32.S | 1 + arch/mips/kernel/scall64-o32.S | 13 ++++++++++++- 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h index 84257df..bf1ac8d3 100644 --- a/arch/mips/include/asm/ptrace.h +++ b/arch/mips/include/asm/ptrace.h @@ -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; diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index 08b639b..9031745 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -19,11 +19,22 @@ #include #include #include +#include + +#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++); diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 7f9bcaa..a17a702 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -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(); diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 1789a80..ffe8913 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -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 diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index 7f5d88b..dd99c328 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -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 diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index b6e1586..f68d2f4 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -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 diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 67dc022..70f6ace 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -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 -- cgit v1.1