riscv: fix seccomp reject syscall code path
If secure_computing() rejected a system call, we were previously setting the system call number to -1, to indicate to later code that the syscall failed. However, if something (e.g. a user notification) was sleeping, and received a signal, we may set a0 to -ERESTARTSYS and re-try the system call again. In this case, seccomp "denies" the syscall (because of the signal), and we would set a7 to -1, thus losing the value of the system call we want to restart. Instead, let's return -1 from do_syscall_trace_enter() to indicate that the syscall was rejected, so we don't clobber the value in case of -ERESTARTSYS or whatever. This commit fixes the user_notification_signal seccomp selftest on riscv to no longer hang. That test expects the system call to be re-issued after the signal, and it wasn't due to the above bug. Now that it is, everything works normally. Note that in the ptrace (tracer) case, the tracer can set the register values to whatever they want, so we still need to keep the code that handles out-of-bounds syscalls. However, we can drop the comment. We can also drop syscall_set_nr(), since it is no longer used anywhere, and the code that re-loads the value in a7 because of it. Reported in: https://lore.kernel.org/bpf/CAEn-LTp=ss0Dfv6J00=rCAy+N78U2AmhqJNjfqjr2FDpPYjxEQ@mail.gmail.com/ Reported-by: David Abdurachmanov <david.abdurachmanov@gmail.com> Signed-off-by: Tycho Andersen <tycho@tycho.ws> Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
This commit is contained in:
Родитель
0a91330b2a
Коммит
af33d2433b
|
@ -28,13 +28,6 @@ static inline int syscall_get_nr(struct task_struct *task,
|
||||||
return regs->a7;
|
return regs->a7;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void syscall_set_nr(struct task_struct *task,
|
|
||||||
struct pt_regs *regs,
|
|
||||||
int sysno)
|
|
||||||
{
|
|
||||||
regs->a7 = sysno;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void syscall_rollback(struct task_struct *task,
|
static inline void syscall_rollback(struct task_struct *task,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -228,20 +228,13 @@ check_syscall_nr:
|
||||||
/* Check to make sure we don't jump to a bogus syscall number. */
|
/* Check to make sure we don't jump to a bogus syscall number. */
|
||||||
li t0, __NR_syscalls
|
li t0, __NR_syscalls
|
||||||
la s0, sys_ni_syscall
|
la s0, sys_ni_syscall
|
||||||
/*
|
|
||||||
* The tracer can change syscall number to valid/invalid value.
|
|
||||||
* We use syscall_set_nr helper in syscall_trace_enter thus we
|
|
||||||
* cannot trust the current value in a7 and have to reload from
|
|
||||||
* the current task pt_regs.
|
|
||||||
*/
|
|
||||||
REG_L a7, PT_A7(sp)
|
|
||||||
/*
|
/*
|
||||||
* Syscall number held in a7.
|
* Syscall number held in a7.
|
||||||
* If syscall number is above allowed value, redirect to ni_syscall.
|
* If syscall number is above allowed value, redirect to ni_syscall.
|
||||||
*/
|
*/
|
||||||
bge a7, t0, 1f
|
bge a7, t0, 1f
|
||||||
/*
|
/*
|
||||||
* Check if syscall is rejected by tracer or seccomp, i.e., a7 == -1.
|
* Check if syscall is rejected by tracer, i.e., a7 == -1.
|
||||||
* If yes, we pretend it was executed.
|
* If yes, we pretend it was executed.
|
||||||
*/
|
*/
|
||||||
li t1, -1
|
li t1, -1
|
||||||
|
@ -334,6 +327,7 @@ work_resched:
|
||||||
handle_syscall_trace_enter:
|
handle_syscall_trace_enter:
|
||||||
move a0, sp
|
move a0, sp
|
||||||
call do_syscall_trace_enter
|
call do_syscall_trace_enter
|
||||||
|
move t0, a0
|
||||||
REG_L a0, PT_A0(sp)
|
REG_L a0, PT_A0(sp)
|
||||||
REG_L a1, PT_A1(sp)
|
REG_L a1, PT_A1(sp)
|
||||||
REG_L a2, PT_A2(sp)
|
REG_L a2, PT_A2(sp)
|
||||||
|
@ -342,6 +336,7 @@ handle_syscall_trace_enter:
|
||||||
REG_L a5, PT_A5(sp)
|
REG_L a5, PT_A5(sp)
|
||||||
REG_L a6, PT_A6(sp)
|
REG_L a6, PT_A6(sp)
|
||||||
REG_L a7, PT_A7(sp)
|
REG_L a7, PT_A7(sp)
|
||||||
|
bnez t0, ret_from_syscall_rejected
|
||||||
j check_syscall_nr
|
j check_syscall_nr
|
||||||
handle_syscall_trace_exit:
|
handle_syscall_trace_exit:
|
||||||
move a0, sp
|
move a0, sp
|
||||||
|
|
|
@ -148,21 +148,19 @@ long arch_ptrace(struct task_struct *child, long request,
|
||||||
* Allows PTRACE_SYSCALL to work. These are called from entry.S in
|
* Allows PTRACE_SYSCALL to work. These are called from entry.S in
|
||||||
* {handle,ret_from}_syscall.
|
* {handle,ret_from}_syscall.
|
||||||
*/
|
*/
|
||||||
__visible void do_syscall_trace_enter(struct pt_regs *regs)
|
__visible int do_syscall_trace_enter(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||||
if (tracehook_report_syscall_entry(regs))
|
if (tracehook_report_syscall_entry(regs))
|
||||||
syscall_set_nr(current, regs, -1);
|
return -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do the secure computing after ptrace; failures should be fast.
|
* Do the secure computing after ptrace; failures should be fast.
|
||||||
* If this fails we might have return value in a0 from seccomp
|
* If this fails we might have return value in a0 from seccomp
|
||||||
* (via SECCOMP_RET_ERRNO/TRACE).
|
* (via SECCOMP_RET_ERRNO/TRACE).
|
||||||
*/
|
*/
|
||||||
if (secure_computing() == -1) {
|
if (secure_computing() == -1)
|
||||||
syscall_set_nr(current, regs, -1);
|
return -1;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||||
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
|
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
|
||||||
|
@ -170,6 +168,7 @@ __visible void do_syscall_trace_enter(struct pt_regs *regs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
audit_syscall_entry(regs->a7, regs->a0, regs->a1, regs->a2, regs->a3);
|
audit_syscall_entry(regs->a7, regs->a0, regs->a1, regs->a2, regs->a3);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__visible void do_syscall_trace_exit(struct pt_regs *regs)
|
__visible void do_syscall_trace_exit(struct pt_regs *regs)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче