diff options
author | Eric W. Biederman | 2021-06-23 16:44:32 -0500 |
---|---|---|
committer | Eric W. Biederman | 2021-08-26 10:30:12 -0500 |
commit | 307d522f5eb86cd6ac8c905f5b0577dedac54ec5 (patch) | |
tree | 826a8de8128133767b650120882b4edea7ee728b | |
parent | a3616a3c02722d1edb95acc7fceade242f6553ba (diff) |
signal/seccomp: Refactor seccomp signal and coredump generation
Factor out force_sig_seccomp from the seccomp signal generation and
place it in kernel/signal.c. The function force_sig_seccomp takes a
parameter force_coredump to indicate that the sigaction field should
be reset to SIGDFL so that a coredump will be generated when the
signal is delivered.
force_sig_seccomp is then used to replace both seccomp_send_sigsys
and seccomp_init_siginfo.
force_sig_info_to_task gains an extra parameter to force using
the default signal action.
With this change seccomp is no longer a special case and there
becomes exactly one place do_coredump is called from.
Further it no longer becomes necessary for __seccomp_filter
to call do_group_exit.
Acked-by: Kees Cook <keescook@chromium.org>
Link: https://lkml.kernel.org/r/87r1gr6qc4.fsf_-_@disp2133
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r-- | include/linux/sched/signal.h | 1 | ||||
-rw-r--r-- | kernel/seccomp.c | 40 | ||||
-rw-r--r-- | kernel/signal.c | 30 |
3 files changed, 33 insertions, 38 deletions
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index b9126fe06c3f..e3c933facd86 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -329,6 +329,7 @@ int force_sig_pkuerr(void __user *addr, u32 pkey); int force_sig_perf(void __user *addr, u32 type, u64 sig_data); int force_sig_ptrace_errno_trap(int errno, void __user *addr); +int force_sig_seccomp(int syscall, int reason, bool force_coredump); extern int send_sig_info(int, struct kernel_siginfo *, struct task_struct *); extern void force_sigsegv(int sig); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 057e17f3215d..abcbd3d2ba54 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -922,30 +922,6 @@ void get_seccomp_filter(struct task_struct *tsk) refcount_inc(&orig->users); } -static void seccomp_init_siginfo(kernel_siginfo_t *info, int syscall, int reason) -{ - clear_siginfo(info); - info->si_signo = SIGSYS; - info->si_code = SYS_SECCOMP; - info->si_call_addr = (void __user *)KSTK_EIP(current); - info->si_errno = reason; - info->si_arch = syscall_get_arch(current); - info->si_syscall = syscall; -} - -/** - * seccomp_send_sigsys - signals the task to allow in-process syscall emulation - * @syscall: syscall number to send to userland - * @reason: filter-supplied reason code to send to userland (via si_errno) - * - * Forces a SIGSYS with a code of SYS_SECCOMP and related sigsys info. - */ -static void seccomp_send_sigsys(int syscall, int reason) -{ - struct kernel_siginfo info; - seccomp_init_siginfo(&info, syscall, reason); - force_sig_info(&info); -} #endif /* CONFIG_SECCOMP_FILTER */ /* For use with seccomp_actions_logged */ @@ -1218,7 +1194,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, /* Show the handler the original registers. */ syscall_rollback(current, current_pt_regs()); /* Let the filter pass back 16 bits of data. */ - seccomp_send_sigsys(this_syscall, data); + force_sig_seccomp(this_syscall, data, false); goto skip; case SECCOMP_RET_TRACE: @@ -1289,18 +1265,14 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, /* Dump core only if this is the last remaining thread. */ if (action != SECCOMP_RET_KILL_THREAD || get_nr_threads(current) == 1) { - kernel_siginfo_t info; - /* Show the original registers in the dump. */ syscall_rollback(current, current_pt_regs()); - /* Trigger a manual coredump since do_exit skips it. */ - seccomp_init_siginfo(&info, this_syscall, data); - do_coredump(&info); - } - if (action == SECCOMP_RET_KILL_THREAD) + /* Trigger a coredump with SIGSYS */ + force_sig_seccomp(this_syscall, data, true); + } else { do_exit(SIGSYS); - else - do_group_exit(SIGSYS); + } + return -1; /* skip the syscall go directly to signal handling */ } unreachable(); diff --git a/kernel/signal.c b/kernel/signal.c index a3229add4455..fbf941b80dd4 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -54,6 +54,7 @@ #include <asm/unistd.h> #include <asm/siginfo.h> #include <asm/cacheflush.h> +#include <asm/syscall.h> /* for syscall_get_* */ /* * SLAB caches for signal bits. @@ -1322,7 +1323,7 @@ int do_send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p * that is why we also clear SIGNAL_UNKILLABLE. */ static int -force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t) +force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool sigdfl) { unsigned long int flags; int ret, blocked, ignored; @@ -1333,7 +1334,7 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t) action = &t->sighand->action[sig-1]; ignored = action->sa.sa_handler == SIG_IGN; blocked = sigismember(&t->blocked, sig); - if (blocked || ignored) { + if (blocked || ignored || sigdfl) { action->sa.sa_handler = SIG_DFL; if (blocked) { sigdelset(&t->blocked, sig); @@ -1354,7 +1355,7 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t) int force_sig_info(struct kernel_siginfo *info) { - return force_sig_info_to_task(info, current); + return force_sig_info_to_task(info, current, false); } /* @@ -1685,7 +1686,7 @@ int force_sig_fault_to_task(int sig, int code, void __user *addr info.si_flags = flags; info.si_isr = isr; #endif - return force_sig_info_to_task(&info, t); + return force_sig_info_to_task(&info, t, false); } int force_sig_fault(int sig, int code, void __user *addr @@ -1793,6 +1794,27 @@ int force_sig_perf(void __user *addr, u32 type, u64 sig_data) return force_sig_info(&info); } +/** + * force_sig_seccomp - signals the task to allow in-process syscall emulation + * @syscall: syscall number to send to userland + * @reason: filter-supplied reason code to send to userland (via si_errno) + * + * Forces a SIGSYS with a code of SYS_SECCOMP and related sigsys info. + */ +int force_sig_seccomp(int syscall, int reason, bool force_coredump) +{ + struct kernel_siginfo info; + + clear_siginfo(&info); + info.si_signo = SIGSYS; + info.si_code = SYS_SECCOMP; + info.si_call_addr = (void __user *)KSTK_EIP(current); + info.si_errno = reason; + info.si_arch = syscall_get_arch(current); + info.si_syscall = syscall; + return force_sig_info_to_task(&info, current, force_coredump); +} + /* For the crazy architectures that include trap information in * the errno field, instead of an actual errno value. */ |