Add a PTRACE_SET_SYSCALL ptrace operation to allow the system call to be cancelled independently to the value of the v0 system call number register. This is needed for SECCOMP_RET_TRACE when the tracer wants to cancel the system call, since it has to set both the system call number to -1 and the chosen return value, both of which reside in the same register (v0). The tracer should set the return value first, followed by PTRACE_SET_SYSCALL to set the system call number to -1. That is in contrast to the normal ptrace syscall hook which triggers the tracer on both entry and exit, allowing the system call to be cancelled during the entry hook (setting system call number register to -1, or optionally using PTRACE_SET_SYSCALL), separately to setting the return value during the exit hook. Positive values (to change the syscall that should be executed instead of cancelling it entirely) are explicitly disallowed at the moment. The same thing can be done safely already by writing the v0 system call number register and the argument registers, and allowing thread_info::syscall to be changed to a different value independently of the v0 register would potentially allow seccomp or the syscall trace events to be fooled into thinking a different system call was being executed. Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx> Cc: Ralf Baechle <ralf@xxxxxxxxxxxxxx> Cc: Oleg Nesterov <oleg@xxxxxxxxxx> Cc: Kees Cook <keescook@xxxxxxxxxxxx> Cc: Andy Lutomirski <luto@xxxxxxxxxxxxxx> Cc: Will Drewry <wad@xxxxxxxxxxxx> Cc: linux-mips@xxxxxxxxxxxxxx --- arch/mips/include/uapi/asm/ptrace.h | 1 + arch/mips/kernel/ptrace.c | 11 +++++++++++ arch/mips/kernel/ptrace32.c | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/arch/mips/include/uapi/asm/ptrace.h b/arch/mips/include/uapi/asm/ptrace.h index 91a3d197ede3..23af103c4e8d 100644 --- a/arch/mips/include/uapi/asm/ptrace.h +++ b/arch/mips/include/uapi/asm/ptrace.h @@ -58,6 +58,7 @@ struct pt_regs { #define PTRACE_GET_THREAD_AREA 25 #define PTRACE_SET_THREAD_AREA 26 +#define PTRACE_SET_SYSCALL 27 /* Calls to trace a 64bit program from a 32bit program. */ #define PTRACE_PEEKTEXT_3264 0xc0 diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 465fc5633e61..9bf31a990c6e 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -853,6 +853,17 @@ long arch_ptrace(struct task_struct *child, long request, ret = put_user(task_thread_info(child)->tp_value, datalp); break; + case PTRACE_SET_SYSCALL: + /* + * This is currently only useful to cancel the syscall from a + * seccomp RET_TRACE tracer. + */ + if ((long)data >= 0) + return -EINVAL; + task_thread_info(child)->syscall = -1; + ret = 0; + break; + case PTRACE_GET_WATCH_REGS: ret = ptrace_get_watch_regs(child, addrp); break; diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 2b9260f92ccd..cca76aec9c10 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -287,6 +287,17 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, (unsigned int __user *) (unsigned long) data); break; + case PTRACE_SET_SYSCALL: + /* + * This is currently only useful to cancel the syscall from a + * seccomp RET_TRACE tracer. + */ + if ((long)data >= 0) + return -EINVAL; + task_thread_info(child)->syscall = -1; + ret = 0; + break; + case PTRACE_GET_THREAD_AREA_3264: ret = put_user(task_thread_info(child)->tp_value, (unsigned long __user *) (unsigned long) data); -- 2.13.2