fcb116bc43
Recently to prevent issues with SECCOMP_RET_KILL and similar signals being changed before they are delivered SA_IMMUTABLE was added. Unfortunately this broke debuggers[1][2] which reasonably expect to be able to trap synchronous SIGTRAP and SIGSEGV even when the target process is not configured to handle those signals. Add force_exit_sig and use it instead of force_fatal_sig where historically the code has directly called do_exit. This has the implementation benefits of going through the signal exit path (including generating core dumps) without the danger of allowing userspace to ignore or change these signals. This avoids userspace regressions as older kernels exited with do_exit which debuggers also can not intercept. In the future is should be possible to improve the quality of implementation of the kernel by changing some of these force_exit_sig calls to force_fatal_sig. That can be done where it matters on a case-by-case basis with careful analysis. Reported-by: Kyle Huey <me@kylehuey.com> Reported-by: kernel test robot <oliver.sang@intel.com> [1] https://lkml.kernel.org/r/CAP045AoMY4xf8aC_4QU_-j7obuEPYgTcnQQP3Yxk=2X90jtpjw@mail.gmail.com [2] https://lkml.kernel.org/r/20211117150258.GB5403@xsang-OptiPlex-9020 Fixes:00b06da29c
("signal: Add SA_IMMUTABLE to ensure forced siganls do not get changed") Fixes:a3616a3c02
("signal/m68k: Use force_sigsegv(SIGSEGV) in fpsp040_die") Fixes:83a1f27ad7
("signal/powerpc: On swapcontext failure force SIGSEGV") Fixes:9bc508cf07
("signal/s390: Use force_sigsegv in default_trap_handler") Fixes:086ec444f8
("signal/sparc32: In setup_rt_frame and setup_fram use force_fatal_sig") Fixes:c317d306d5
("signal/sparc32: Exit with a fatal signal when try_to_clear_window_buffer fails") Fixes:695dd0d634
("signal/x86: In emulate_vsyscall force a signal instead of calling do_exit") Fixes:1fbd60df8a
("signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.") Fixes:941edc5bf1
("exit/syscall_user_dispatch: Send ordinary signals on failure") Link: https://lkml.kernel.org/r/871r3dqfv8.fsf_-_@email.froward.int.ebiederm.org Reviewed-by: Kees Cook <keescook@chromium.org> Tested-by: Kees Cook <keescook@chromium.org> Tested-by: Kyle Huey <khuey@kylehuey.com> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
109 lines
2.4 KiB
C
109 lines
2.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020 Collabora Ltd.
|
|
*/
|
|
#include <linux/sched.h>
|
|
#include <linux/prctl.h>
|
|
#include <linux/syscall_user_dispatch.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/elf.h>
|
|
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/sched/task_stack.h>
|
|
|
|
#include <asm/syscall.h>
|
|
|
|
#include "common.h"
|
|
|
|
static void trigger_sigsys(struct pt_regs *regs)
|
|
{
|
|
struct kernel_siginfo info;
|
|
|
|
clear_siginfo(&info);
|
|
info.si_signo = SIGSYS;
|
|
info.si_code = SYS_USER_DISPATCH;
|
|
info.si_call_addr = (void __user *)KSTK_EIP(current);
|
|
info.si_errno = 0;
|
|
info.si_arch = syscall_get_arch(current);
|
|
info.si_syscall = syscall_get_nr(current, regs);
|
|
|
|
force_sig_info(&info);
|
|
}
|
|
|
|
bool syscall_user_dispatch(struct pt_regs *regs)
|
|
{
|
|
struct syscall_user_dispatch *sd = ¤t->syscall_dispatch;
|
|
char state;
|
|
|
|
if (likely(instruction_pointer(regs) - sd->offset < sd->len))
|
|
return false;
|
|
|
|
if (unlikely(arch_syscall_is_vdso_sigreturn(regs)))
|
|
return false;
|
|
|
|
if (likely(sd->selector)) {
|
|
/*
|
|
* access_ok() is performed once, at prctl time, when
|
|
* the selector is loaded by userspace.
|
|
*/
|
|
if (unlikely(__get_user(state, sd->selector))) {
|
|
force_exit_sig(SIGSEGV);
|
|
return true;
|
|
}
|
|
|
|
if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
|
|
return false;
|
|
|
|
if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
|
|
force_exit_sig(SIGSYS);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
sd->on_dispatch = true;
|
|
syscall_rollback(current, regs);
|
|
trigger_sigsys(regs);
|
|
|
|
return true;
|
|
}
|
|
|
|
int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
|
|
unsigned long len, char __user *selector)
|
|
{
|
|
switch (mode) {
|
|
case PR_SYS_DISPATCH_OFF:
|
|
if (offset || len || selector)
|
|
return -EINVAL;
|
|
break;
|
|
case PR_SYS_DISPATCH_ON:
|
|
/*
|
|
* Validate the direct dispatcher region just for basic
|
|
* sanity against overflow and a 0-sized dispatcher
|
|
* region. If the user is able to submit a syscall from
|
|
* an address, that address is obviously valid.
|
|
*/
|
|
if (offset && offset + len <= offset)
|
|
return -EINVAL;
|
|
|
|
if (selector && !access_ok(selector, sizeof(*selector)))
|
|
return -EFAULT;
|
|
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
current->syscall_dispatch.selector = selector;
|
|
current->syscall_dispatch.offset = offset;
|
|
current->syscall_dispatch.len = len;
|
|
current->syscall_dispatch.on_dispatch = false;
|
|
|
|
if (mode == PR_SYS_DISPATCH_ON)
|
|
set_syscall_work(SYSCALL_USER_DISPATCH);
|
|
else
|
|
clear_syscall_work(SYSCALL_USER_DISPATCH);
|
|
|
|
return 0;
|
|
}
|