Skip to content

Commit

Permalink
MIPS: seccomp: Handle indirect system calls (o32)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Markos Chandras authored and Ralf Baechle committed Mar 26, 2014
1 parent 9d37c40 commit 4c21b8f
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 6 deletions.
2 changes: 1 addition & 1 deletion arch/mips/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 19 additions & 1 deletion arch/mips/include/asm/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,22 @@
#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,
Expand Down Expand Up @@ -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++);
Expand Down
3 changes: 1 addition & 2 deletions arch/mips/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
11 changes: 10 additions & 1 deletion arch/mips/kernel/scall32-o32.S
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions arch/mips/kernel/scall64-64.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/mips/kernel/scall64-n32.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 12 additions & 1 deletion arch/mips/kernel/scall64-o32.S
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 4c21b8f

Please sign in to comment.