]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
signal: Replace force_fatal_sig with force_exit_sig when in doubt
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 18 Nov 2021 20:23:21 +0000 (14:23 -0600)
committerAndrea Righi <andrea.righi@canonical.com>
Tue, 4 Jan 2022 08:48:55 +0000 (09:48 +0100)
BugLink: https://bugs.launchpad.net/bugs/1952579
commit fcb116bc43c8c37c052530ead79872f8b2615711 upstream.

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: 00b06da29cf9 ("signal: Add SA_IMMUTABLE to ensure forced siganls do not get changed")
Fixes: a3616a3c0272 ("signal/m68k: Use force_sigsegv(SIGSEGV) in fpsp040_die")
Fixes: 83a1f27ad773 ("signal/powerpc: On swapcontext failure force SIGSEGV")
Fixes: 9bc508cf0791 ("signal/s390: Use force_sigsegv in default_trap_handler")
Fixes: 086ec444f866 ("signal/sparc32: In setup_rt_frame and setup_fram use force_fatal_sig")
Fixes: c317d306d550 ("signal/sparc32: Exit with a fatal signal when try_to_clear_window_buffer fails")
Fixes: 695dd0d634df ("signal/x86: In emulate_vsyscall force a signal instead of calling do_exit")
Fixes: 1fbd60df8a85 ("signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.")
Fixes: 941edc5bf174 ("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>
Cc: Thomas Backlund <tmb@iki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
arch/m68k/kernel/traps.c
arch/powerpc/kernel/signal_32.c
arch/powerpc/kernel/signal_64.c
arch/s390/kernel/traps.c
arch/sparc/kernel/signal_32.c
arch/sparc/kernel/windows.c
arch/x86/entry/vsyscall/vsyscall_64.c
arch/x86/kernel/vm86_32.c
include/linux/sched/signal.h
kernel/entry/syscall_user_dispatch.c
kernel/signal.c

index 99058a6da956da2f4c10ace2fc45cc6e7ff19cef..34d6458340b0f79097a7e0f17326012a7823c042 100644 (file)
@@ -1145,7 +1145,7 @@ asmlinkage void set_esp0(unsigned long ssp)
  */
 asmlinkage void fpsp040_die(void)
 {
-       force_fatal_sig(SIGSEGV);
+       force_exit_sig(SIGSEGV);
 }
 
 #ifdef CONFIG_M68KFPU_EMU
index 933ab95805a6c43729b1699d5bc9646d94382051..f2da879264bcd09ba9308d7b57d320effc2e2a62 100644 (file)
@@ -1063,7 +1063,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
         * We kill the task with a SIGSEGV in this situation.
         */
        if (do_setcontext(new_ctx, regs, 0)) {
-               force_fatal_sig(SIGSEGV);
+               force_exit_sig(SIGSEGV);
                return -EFAULT;
        }
 
index 8ead9b3f47c61f040c6e56aa580d8b265c18d9e2..bb9c077ac13220094a06c3c93daead8c3865016a 100644 (file)
@@ -704,7 +704,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
         */
 
        if (__get_user_sigset(&set, &new_ctx->uc_sigmask)) {
-               force_fatal_sig(SIGSEGV);
+               force_exit_sig(SIGSEGV);
                return -EFAULT;
        }
        set_current_blocked(&set);
@@ -713,7 +713,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
                return -EFAULT;
        if (__unsafe_restore_sigcontext(current, NULL, 0, &new_ctx->uc_mcontext)) {
                user_read_access_end();
-               force_fatal_sig(SIGSEGV);
+               force_exit_sig(SIGSEGV);
                return -EFAULT;
        }
        user_read_access_end();
index 01a7c68dcfb6ee22142c599c15de05513763cf8f..12d28ff5281fada3513a6f89bfb18fae1119ce95 100644 (file)
@@ -84,7 +84,7 @@ static void default_trap_handler(struct pt_regs *regs)
 {
        if (user_mode(regs)) {
                report_user_fault(regs, SIGSEGV, 0);
-               force_fatal_sig(SIGSEGV);
+               force_exit_sig(SIGSEGV);
        } else
                die(regs, "Unknown program exception");
 }
index cd677bc564a7e279f4f40b2a541749f1d7f5c5f9..ffab16369beac82f9fd6cfe243571888f9609a81 100644 (file)
@@ -244,7 +244,7 @@ static int setup_frame(struct ksignal *ksig, struct pt_regs *regs,
                get_sigframe(ksig, regs, sigframe_size);
 
        if (invalid_frame_pointer(sf, sigframe_size)) {
-               force_fatal_sig(SIGILL);
+               force_exit_sig(SIGILL);
                return -EINVAL;
        }
 
@@ -336,7 +336,7 @@ static int setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs,
        sf = (struct rt_signal_frame __user *)
                get_sigframe(ksig, regs, sigframe_size);
        if (invalid_frame_pointer(sf, sigframe_size)) {
-               force_fatal_sig(SIGILL);
+               force_exit_sig(SIGILL);
                return -EINVAL;
        }
 
index bbbd40cc6b282ad7e42ddffe174170ebccd68c7d..8f20862ccc83e77fd01d12cf4856d01455a37564 100644 (file)
@@ -122,7 +122,7 @@ void try_to_clear_window_buffer(struct pt_regs *regs, int who)
                if ((sp & 7) ||
                    copy_to_user((char __user *) sp, &tp->reg_window[window],
                                 sizeof(struct reg_window32))) {
-                       force_fatal_sig(SIGILL);
+                       force_exit_sig(SIGILL);
                        return;
                }
        }
index 0b6b277ee050b5084a4e74335c70ee637be5b21d..fd2ee9408e914a20a4b50a6cdb1249e297e913c2 100644 (file)
@@ -226,7 +226,7 @@ bool emulate_vsyscall(unsigned long error_code,
        if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
                warn_bad_vsyscall(KERN_DEBUG, regs,
                                  "seccomp tried to change syscall nr or ip");
-               force_fatal_sig(SIGSYS);
+               force_exit_sig(SIGSYS);
                return true;
        }
        regs->orig_ax = -1;
index 955349516fdd1e5b103d7e7eb6ecf75b763081e8..17d58740891e207d8e3cdb72399f5072bbb85c4f 100644 (file)
@@ -162,7 +162,7 @@ Efault_end:
        user_access_end();
 Efault:
        pr_alert("could not access userspace vm86 info\n");
-       force_fatal_sig(SIGSEGV);
+       force_exit_sig(SIGSEGV);
        goto exit_vm86;
 }
 
index e2dc9f119adae95e5190419d702e1ccf46720255..9a707b555b0a07cd9292433086b89ff230279f11 100644 (file)
@@ -339,6 +339,7 @@ extern __must_check bool do_notify_parent(struct task_struct *, int);
 extern void __wake_up_parent(struct task_struct *p, struct task_struct *parent);
 extern void force_sig(int);
 extern void force_fatal_sig(int);
+extern void force_exit_sig(int);
 extern int send_sig(int, struct task_struct *, int);
 extern int zap_other_threads(struct task_struct *p);
 extern struct sigqueue *sigqueue_alloc(void);
index 4508201847d22ec7b7f7749693fafc1b8453ba5b..0b6379adff6bdd4327ace5ae7029cfd24369e825 100644 (file)
@@ -48,7 +48,7 @@ bool syscall_user_dispatch(struct pt_regs *regs)
                 * the selector is loaded by userspace.
                 */
                if (unlikely(__get_user(state, sd->selector))) {
-                       force_fatal_sig(SIGSEGV);
+                       force_exit_sig(SIGSEGV);
                        return true;
                }
 
@@ -56,7 +56,7 @@ bool syscall_user_dispatch(struct pt_regs *regs)
                        return false;
 
                if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
-                       force_fatal_sig(SIGSYS);
+                       force_exit_sig(SIGSYS);
                        return true;
                }
        }
index e661f8835890e1caa9f5e880ba1339ac0548203c..5892c91696f8418ec23098a688a1a219df72634b 100644 (file)
@@ -1671,6 +1671,19 @@ void force_fatal_sig(int sig)
        force_sig_info_to_task(&info, current, HANDLER_SIG_DFL);
 }
 
+void force_exit_sig(int sig)
+{
+       struct kernel_siginfo info;
+
+       clear_siginfo(&info);
+       info.si_signo = sig;
+       info.si_errno = 0;
+       info.si_code = SI_KERNEL;
+       info.si_pid = 0;
+       info.si_uid = 0;
+       force_sig_info_to_task(&info, current, HANDLER_EXIT);
+}
+
 /*
  * When things go south during signal handling, we
  * will force a SIGSEGV. And if the signal that caused