exit cleanups
cover exit cleanups
While looking at some issues related to the exit path in the kernel I found several instances where the code is not using the existing abstractions properly.
This set of changes introduces force_fatal_sig a way of sending a signal and not allowing it to be caught, and corrects the misuse of the existing abstractions that I found.
A lot of the misuse of the existing abstractions are silly things such as doing something after calling a no return function, rolling BUG by hand, doing more work than necessary to terminate a kernel thread, or calling do_exit(SIGKILL) instead of calling force_sig(SIGKILL).
It is my plan after sending all of these changes out for review to place them in a topic branch for sending Linus. Especially for the changes that depend upon the new helper force_fatal_sig this is important.
Eric W. Biederman (20):
exit/doublefault: Remove apparently bogus comment about rewind_stack_do_exit
exit: Remove calls of do_exit after noreturn versions of die
reboot: Remove the unreachable panic after do_exit in reboot(2) signal/sparc32: Remove unreachable do_exit in do_sparc_fault signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT signal/sh: Use force_sig(SIGKILL) instead of do_group_exit(SIGKILL) signal/powerpc: On swapcontext failure force SIGSEGV signal/sparc: In setup_tsb_params convert open coded BUG into BUG signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved. signal/s390: Use force_sigsegv in default_trap_handler exit/kthread: Have kernel threads return instead of calling do_exit
signal: Implement force_fatal_sig
exit/syscall_user_dispatch: Send ordinary signals on failure signal/sparc32: Exit with a fatal signal when try_to_clear_window_buffer fails signal/sparc32: In setup_rt_frame and setup_fram use force_fatal_sig signal/x86: In emulate_vsyscall force a signal instead of calling do_exit exit/rtl8723bs: Replace the macro thread_exit with a simple return 0 exit/rtl8712: Replace the macro thread_exit with a simple return 0 exit/r8188eu: Replace the macro thread_exit with a simple return 0
arch/mips/kernel/r2300_fpu.S | 4 ++-- arch/mips/kernel/syscall.c | 9 -------- arch/nds32/kernel/traps.c | 2 +- arch/nds32/mm/fault.c | 6 +---- arch/openrisc/kernel/traps.c | 2 +- arch/openrisc/mm/fault.c | 4 +--- arch/powerpc/kernel/signal_32.c | 6 +++-- arch/powerpc/kernel/signal_64.c | 9 +++++--- arch/s390/include/asm/kdebug.h | 2 +- arch/s390/kernel/dumpstack.c | 2 +- arch/s390/kernel/traps.c | 2 +- arch/s390/mm/fault.c | 2 -- arch/sh/kernel/cpu/fpu.c | 10 +++++---- arch/sh/kernel/traps.c | 2 +- arch/sh/mm/fault.c | 2 -- arch/sparc/kernel/signal_32.c | 4 ++-- arch/sparc/kernel/windows.c | 6 +++-- arch/sparc/mm/fault_32.c | 1 - arch/sparc/mm/tsb.c | 2 +- arch/x86/entry/vsyscall/vsyscall_64.c | 3 ++- arch/x86/kernel/doublefault_32.c | 3 --- arch/x86/kernel/signal.c | 6 ++++- arch/x86/kernel/vm86_32.c | 8 +++---- arch/xtensa/kernel/traps.c | 2 +- arch/xtensa/mm/fault.c | 3 +-- drivers/firmware/stratix10-svc.c | 4 ++-- drivers/soc/ti/wkup_m3_ipc.c | 2 +- drivers/staging/r8188eu/core/rtw_cmd.c | 2 +- drivers/staging/r8188eu/core/rtw_mp.c | 2 +- drivers/staging/r8188eu/include/osdep_service.h | 2 -- drivers/staging/rtl8712/osdep_service.h | 1 - drivers/staging/rtl8712/rtl8712_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_xmit.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- .../rtl8723bs/include/osdep_service_linux.h | 2 -- fs/ocfs2/journal.c | 5 +---- include/linux/sched/signal.h | 1 + kernel/entry/syscall_user_dispatch.c | 12 ++++++---- kernel/kthread.c | 2 +- kernel/reboot.c | 1 - kernel/signal.c | 26 ++++++++++++++-------- net/batman-adv/tp_meter.c | 2 +- 43 files changed, 83 insertions(+), 91 deletions(-)
Eric
PATCH 1/20 exit/doublefault: Remove apparently bogus comment about rewind_stack_do_exit
I do not see panic calling rewind_stack_do_exit anywhere, nor can I find anywhere in the history where doublefault_shim has called rewind_stack_do_exit. So I don't think this comment was ever actually correct.
--- arch/x86/kernel/doublefault_32.c | 3 --- 1 file changed, 3 deletions(-)
arch/x86/kernel/doublefault_32.c +0 -3
@@ -77,9 +77,6 @@ asmlinkage noinstr void __noreturn doublefault_shim(void)* some way to reconstruct CR3. We could make a credible guess based * on cpu_tlbstate, but that would be racy and would not account for * PTI. -* -* Instead, don't bother. We can return through -* rewind_stack_do_exit() instead. */ panic ("cannot return from double fault\n" ); }
Signature · 1 line
2.20.1
Re: [PATCH 01/20] exit/doublefault: Remove apparently bogus comment about rewind_stack_do_exit
Quoted 8 lines · Eric W. Biederman
I do not see panic calling
rewind_stack_do_exitanywhere, nor can I find anywhere in the history wheredoublefault_shimhas calledrewind_stack_do_exit. So I don't think this comment was ever actually correct.Cc Andy Lutomirski <[email protected]>Fixes 7d8d8cfdee9a ("x86/doublefault/32: Rewrite the x86_32 #DF handler and unify with 64-bit")Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
PATCH 2/20 exit: Remove calls of do_exit after noreturn versions of die
In reply to Eric W. BiedermanOn nds32, openrisc, s390, sh, and xtensa the function die never returns. Mark die __noreturn so that no one expects die to return. Remove the do_exit calls after die as they will never be reached.
--- arch/nds32/kernel/traps.c | 2 +- arch/nds32/mm/fault.c | 6 +----- arch/openrisc/kernel/traps.c | 2 +- arch/openrisc/mm/fault.c | 4 +--- arch/s390/include/asm/kdebug.h | 2 +- arch/s390/kernel/dumpstack.c | 2 +- arch/s390/mm/fault.c | 2 -- arch/sh/kernel/traps.c | 2 +- arch/sh/mm/fault.c | 2 -- arch/xtensa/kernel/traps.c | 2 +- arch/xtensa/mm/fault.c | 3 +-- 11 files changed, 9 insertions(+), 20 deletions(-)
arch/nds32/kernel/traps.c +1 -1
@@ -118,7 +118,7 @@ DEFINE_SPINLOCK(die_lock);/* * This function is protected against re-entrancy. */ -void die (const char *str,struct pt_regs *regs,int err) +void __noreturn die (const char *str,struct pt_regs *regs,int err) {struct task_struct *tsk = current;static int die_counter;
arch/nds32/mm/fault.c +1 -5
@@ -13,7 +13,7 @@#include <asm/tlbflush.h> -extern void die (const char *str,struct pt_regs *regs,long err); +extern void __noreturn die (const char *str,struct pt_regs *regs,long err);/* * This is useful to dump out the page tables associated with
@@ -299,10 +299,6 @@ void do_page_fault(unsigned long entry, unsigned long addr,show_pte (mm, addr);die ("Oops" , regs, error_code); -bust_spinlocks (0 ); -do_exit (SIGKILL ); - -return ;/* * We ran out of memory, or some other thing happened to us that made
arch/openrisc/kernel/traps.c +1 -1
@@ -197,7 +197,7 @@ void nommu_dump_state(struct pt_regs *regs, }/* This is normally the 'Oops' routine */ -void die (const char *str,struct pt_regs *regs,long err) +void __noreturn die (const char *str,struct pt_regs *regs,long err) {console_verbose ();
arch/openrisc/mm/fault.c +1 -3
@@ -32,7 +32,7 @@ unsigned long pte_errors; /* updated by do_page_fault() */*/ volatile pgd_t *current_pgd[NR_CPUS ]; -extern void die (char *,struct pt_regs *,long ); +extern void __noreturn die (char *,struct pt_regs *,long );/* * This routine handles page faults. It determines the address,
@@ -248,8 +248,6 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,die ("Oops" , regs, write_acc); -do_exit (SIGKILL ); -/* * We ran out of memory, or some other thing happened to us that made * us unable to handle the page fault gracefully.
arch/s390/include/asm/kdebug.h +1 -1
@@ -23,6 +23,6 @@ enum die_val { DIE_NMI_IPI, }; -extern void die (struct pt_regs *,const char *); +extern void __noreturn die (struct pt_regs *,const char *);#endif
arch/s390/kernel/dumpstack.c +1 -1
@@ -192,7 +192,7 @@ void show_regs(struct pt_regs *regs)static DEFINE_SPINLOCK (die_lock); -void die (struct pt_regs *regs,const char *str) +void __noreturn die (struct pt_regs *regs,const char *str) {static int die_counter;
arch/s390/mm/fault.c +0 -2
@@ -260,7 +260,6 @@ static noinline void do_no_context(struct pt_regs *regs) " in virtual user address space\n");dump_fault_info (regs);die (regs,"Oops" ); -do_exit (SIGKILL ); } static noinline void do_low_address(struct pt_regs *regs)
@@ -270,7 +269,6 @@ static noinline void do_low_address(struct pt_regs *regs)if (regs->psw.mask &PSW_MASK_PSTATE ) {/* Low-address protection hit in user mode 'cannot happen'. */ die (regs,"Low-address protection" ); -do_exit (SIGKILL ); }do_no_context (regs);
arch/sh/kernel/traps.c +1 -1
@@ -20,7 +20,7 @@static DEFINE_SPINLOCK (die_lock); -void die (const char *str,struct pt_regs *regs,long err) +void __noreturn die (const char *str,struct pt_regs *regs,long err) {static int die_counter;
arch/sh/mm/fault.c +0 -2
@@ -238,8 +238,6 @@ no_context(struct pt_regs *regs, unsigned long error_code,show_fault_oops (regs, address);die ("Oops" , regs, error_code); -bust_spinlocks (0 ); -do_exit (SIGKILL ); } static void
arch/xtensa/kernel/traps.c +1 -1
@@ -527,7 +527,7 @@ void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)DEFINE_SPINLOCK (die_lock); -void die (const char * str,struct pt_regs * regs,long err) +void __noreturn die (const char * str,struct pt_regs * regs,long err) {static int die_counter;const char *pr ="" ;
arch/xtensa/mm/fault.c +1 -2
@@ -238,7 +238,7 @@ void do_page_fault(struct pt_regs *regs)void bad_page_fault (struct pt_regs *regs,unsigned long address,int sig) { -extern void die (const char *,struct pt_regs *,long ); +extern void __noreturn die (const char *,struct pt_regs *,long );const struct exception_table_entry *entry;/* Are we prepared to handle this kernel fault? */
@@ -257,5 +257,4 @@ bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)"address %08lx\n pc = %08lx, ra = %08lx\n" , address, regs->pc, regs->areg[0]);die ("Oops" , regs, sig); -do_exit (sig); }
Signature · 1 line
2.20.1
Re: [PATCH 02/20] exit: Remove calls of do_exit after noreturn versions of die
Quoted 3 lines · Eric W. Biederman
On nds32, openrisc, s390, sh, and xtensa the function die never returns. Mark die
__noreturnso that no one expects die to return. Remove thedo_exitcalls after die as they will never be reached.
Maybe note that the "bust_spinlocks" calls are also redundant, since they're in die(). I note that is a "mismatch" between the do_kill() in die() (SIGSEGV) and after die() (SIGKILL). This patch makes no behavioral change (the first caller would "win"), but I thought I'd note it in case some architecture would prefer a different signal.
-Kees
Signature · 1 line
Kees Cook
Re: [PATCH 02/20] exit: Remove calls of do_exit after noreturn versions of die
Quoted 10 lines · Kees Cook
Quoted 3 lines · Eric W. Biederman
On nds32, openrisc, s390, sh, and xtensa the function die never returns. Mark die
__noreturnso that no one expects die to return. Remove thedo_exitcalls after die as they will never be reached.Maybe note that the "
bust_spinlocks" calls are also redundant, since they're indie(). I note that is a "mismatch" between thedo_kill()indie()(SIGSEGV) and afterdie()(SIGKILL). This patch makes no behavioral change (the first caller would "win"), but I thought I'd note it in case some architecture would prefer a different signal.
If someone has some strong preferences in the matter of which signal a wait on a processes that has oopsed should return please let me know.
My next step in cleaning up the uses of do_exit looks like it is going to be getting all of the architectures to use the same signal for oopses (aka die), and then introducing a helper (called something like "make_task_dead" or "oops_task_exit" ) that will replace do_exit on the oops path and not take a signal number at all.
That helper I can then remove the ptrace break point from and possibly some of the coredump logic as well. Ultimately it will be something we can optimize for the case when we know there is a kernel bug and we just want the task to exit so the rest of the system can limp along as best as it can.
Eric
PATCH 3/20 reboot: Remove the unreachable panic after do_exit in reboot(2)
In reply to Eric W. Biederman--- kernel/reboot.c | 1 - 1 file changed, 1 deletion(-)
kernel/reboot.c +0 -1
@@ -359,7 +359,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,case LINUX_REBOOT_CMD_HALT :kernel_halt ();do_exit (0 ); -panic ("cannot halt" );case LINUX_REBOOT_CMD_POWER_OFF :kernel_power_off ();
Signature · 1 line
2.20.1
Re: [PATCH 03/20] reboot: Remove the unreachable panic after do_exit in reboot(2)
Quoted 10 lines · Eric W. Biederman
Signed-off-by "Eric W. Biederman" <[email protected]>--- kernel/reboot.c | 1 - 1 file changed, 1 deletion(-)kernel/reboot.c +0
-0
kernel/reboot.c SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, · line 362
Quoted 4 lines
kernel/reboot.c +0
-1@@ -359,7 +359,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,case LINUX_REBOOT_CMD_HALT :kernel_halt ();do_exit (0 ); -panic ("cannot halt" );
This looks like it was here for robustness (i.e. panic if do_exit somehow fails)? But since do_exit() is marked __no_return, it doesn't make sense to keep it. If we wanted to keep _something_ here, maybe just add unreachable() ?
Quoted 6 lines
kernel/reboot.c +0
-0case LINUX_REBOOT_CMD_POWER_OFF :kernel_power_off ();Signature · 1 line
2.20.1
Signature · 1 line
Kees Cook
PATCH 4/20 signal/sparc32: Remove unreachable do_exit in do_sparc_fault
In reply to Eric W. BiedermanThe call to do_exit in do_sparc_fault immediately follows a call to unhandled_fault. The function unhandled_fault never returns. This means the call to do_exit can never be reached.
--- arch/sparc/mm/fault_32.c | 1 - 1 file changed, 1 deletion(-)
arch/sparc/mm/fault_32.c +0 -1
@@ -248,7 +248,6 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, }unhandled_fault (address, tsk, regs); -do_exit (SIGKILL );/* * We ran out of memory, or some other thing happened to us that made
Signature · 1 line
2.20.1
Re: [PATCH 04/20] signal/sparc32: Remove unreachable do_exit in do_sparc_fault
Quoted 3 lines · Eric W. Biederman
The call to
do_exitindo_sparc_faultimmediately follows a call tounhandled_fault. The functionunhandled_faultnever returns. This means the call todo_exitcan never be reached.
Same thought: replace with unreachable() just to make this more self-documenting? Either way:
Quoted 24 lines
Cc David Miller <[email protected]>Fixes 2.3.41Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/sparc/mm/fault_32.c | 1 - 1 file changed, 1 deletion(-)arch/sparc/mm/fault_32.c +0
-1@@ -248,7 +248,6 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, }unhandled_fault (address, tsk, regs); -do_exit (SIGKILL );/* * We ran out of memory, or some other thing happened to us that made Signature · 1 line
2.20.1
Signature · 1 line
Kees Cook
PATCH 5/20 signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT
In reply to Eric W. BiedermanWhen an instruction to save or restore a register from the stack fails in _save_fp_context or _restore_fp_context return with -EFAULT. This change was made to r2300_fpu.S[1] but it looks like it got lost with the introduction of EX2[2]. This is also what the other implementation of _save_fp_context and _restore_fp_context in r4k_fpu.S does, and what is needed for the callers to be able to handle the error.
Furthermore calling do_exit(SIGSEGV) from bad_stack is wrong because it does not terminate the entire process it just terminates a single thread.
As the changed code was the only caller of arch/mips/kernel/syscall.c:bad_stack remove the problematic and now unused helper function.
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Maciej Rozycki <[email protected]>
Cc: [email protected]
[1] 35938a00ba86 ("MIPS: Fix ISA I FP sigcontext access violation handling")
[2] f92722dc4545 ("MIPS: Correct MIPS I FP sigcontext layout")
--- arch/mips/kernel/r2300_fpu.S | 4 ++-- arch/mips/kernel/syscall.c | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-)
arch/mips/kernel/r2300_fpu.S +2 -2
@@ -29,8 +29,8 @@ #define EX2(a,b) \ 9: a,##b; \ .section __ex_table,"a"; \ - PTR 9b,bad_stack; \ - PTR 9b+4,bad_stack; \ + PTR 9b,fault; \ + PTR 9b+4,fault; \ .previous .set mips1
arch/mips/kernel/syscall.c +0 -9
@@ -240,12 +240,3 @@ SYSCALL_DEFINE3(cachectl, char *, addr, int, nbytes, int, op) {return -ENOSYS ; } - -/* -* If we ever come here the user sp is bad. Zap the process right away. -* Due to the bad stack signaling wouldn't work. -*/ -asmlinkage void bad_stack(void) -{ -do_exit (SIGSEGV ); -}
Signature · 1 line
2.20.1
Re: [PATCH 05/20] signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT
Quoted 14 lines · Eric W. Biederman
When an instruction to save or restore a register from the stack fails in
_save_fp_contextor_restore_fp_contextreturn with-EFAULT. This change was made to r2300_fpu.S[1] but it looks like it got lost with the introduction of EX2[2]. This is also what the other implementation of_save_fp_contextand_restore_fp_contextinr4k_fpu.Sdoes, and what is needed for the callers to be able to handle the error.Furthermore calling
do_exit(SIGSEGV)frombad_stackis wrong because it does not terminate the entire process it just terminates a single thread.As the changed code was the only caller of arch/mips/kernel/syscall.c:bad_stack remove the problematic and now unused helper function.
Quoted 7 lines
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Maciej Rozycki <[email protected]>
Cc: [email protected]
[1]35938a00ba86("MIPS: Fix ISA I FP sigcontext access violation handling")
[2]f92722dc4545("MIPS: Correct MIPS I FP sigcontext layout")Fixes f92722dc4545 ("MIPS: Correct MIPS I FP sigcontext layout")Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
Re: [PATCH 05/20] signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT
In reply to Eric W. BiedermanQuoted 6 lines · Eric W. Biederman
When an instruction to save or restore a register from the stack fails in
_save_fp_contextor_restore_fp_contextreturn with-EFAULT. This change was made to r2300_fpu.S[1] but it looks like it got lost with the introduction of EX2[2]. This is also what the other implementation of_save_fp_contextand_restore_fp_contextinr4k_fpu.Sdoes, and what is needed for the callers to be able to handle the error.
Umm, right, good catch, thanks! I think this ought to be backported.
Maciej
Re: [PATCH 05/20] signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT
Quoted 9 lines · Maciej W. Rozycki
Quoted 6 lines · Eric W. Biederman
When an instruction to save or restore a register from the stack fails in
_save_fp_contextor_restore_fp_contextreturn with-EFAULT. This change was made to r2300_fpu.S[1] but it looks like it got lost with the introduction of EX2[2]. This is also what the other implementation of_save_fp_contextand_restore_fp_contextinr4k_fpu.Sdoes, and what is needed for the callers to be able to handle the error.
Quoted 4 lines
Umm, right, good catch, thanks! I think this ought to be backported.
I will add a CC stable. So it can be backported after it is merged.
Eric
Re: [PATCH 05/20] signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT
In reply to Eric W. BiedermanQuoted 19 lines · Eric W. Biederman
When an instruction to save or restore a register from the stack fails in
_save_fp_contextor_restore_fp_contextreturn with-EFAULT. This change was made to r2300_fpu.S[1] but it looks like it got lost with the introduction of EX2[2]. This is also what the other implementation of_save_fp_contextand_restore_fp_contextinr4k_fpu.Sdoes, and what is needed for the callers to be able to handle the error.Furthermore calling
do_exit(SIGSEGV)frombad_stackis wrong because it does not terminate the entire process it just terminates a single thread.As the changed code was the only caller of arch/mips/kernel/syscall.c:bad_stack remove the problematic and now unused helper function.
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Maciej Rozycki <[email protected]>
Cc: [email protected]
[1]35938a00ba86("MIPS: Fix ISA I FP sigcontext access violation handling")
[2]f92722dc4545("MIPS: Correct MIPS I FP sigcontext layout")
Quoted 6 lines
Fixes f92722dc4545 ("MIPS: Correct MIPS I FP sigcontext layout")Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/mips/kernel/r2300_fpu.S | 4 ++-- arch/mips/kernel/syscall.c | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-)
Signature · 2 lines
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a good idea. [ RFC1925, 2.3 ]
PATCH 6/20 signal/sh: Use force_sig(SIGKILL) instead of do_group_exit(SIGKILL)
In reply to Eric W. BiedermanToday the sh code allocates memory the first time a process uses the fpu. If that memory allocation fails, kill the affected task with force_sig(SIGKILL) rather than do_group_exit(SIGKILL).
Calling do_group_exit from an exception handler can potentially lead to dead locks as do_group_exit is not designed to be called from interrupt context. Instead use force_sig(SIGKILL) to kill the userspace process. Sending signals in general and force_sig in particular has been tested from interrupt context so there should be no problems.
--- arch/sh/kernel/cpu/fpu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
arch/sh/kernel/cpu/fpu.c +6 -4
@@ -62,18 +62,20 @@ void fpu_state_restore(struct pt_regs *regs) }if (!tsk_used_math (tsk)) { -local_irq_enable (); +int ret;/* * does a slab alloc which can sleep */ -if (init_fpu (tsk)) { +local_irq_enable (); + ret =init_fpu (tsk); +local_irq_disable (); +if (ret) {/* * ran out of memory! */ -do_group_exit (SIGKILL ); +force_sig (SIGKILL );return ; } -local_irq_disable (); }grab_fpu (regs);
Signature · 1 line
2.20.1
Re: [PATCH 06/20] signal/sh: Use force_sig(SIGKILL) instead of do_group_exit(SIGKILL)
Quoted 2 lines · Eric W. Biederman
+
force_sig(SIGKILL);
I wonder if SIGFPE would be a more intuitive thing.
Doesn't really matter, this is a "doesn't happen" event anyway, but that was just my reaction to reading the patch.
Linus
Rich Felker
maintainerRe: [PATCH 06/20] signal/sh: Use force_sig(SIGKILL) instead of do_group_exit(SIGKILL)
Quoted 8 lines · Linus Torvalds
Quoted 2 lines · Eric W. Biederman
+
force_sig(SIGKILL);I wonder if SIGFPE would be a more intuitive thing.
Doesn't really matter, this is a "doesn't happen" event anyway, but that was just my reaction to reading the patch.
I think SIGKILL makes more sense unless there's a way the process could handle the resulting SIGFPE and recover. I'd actually like to see the lazy allocation of FPU state just removed (the amount of space saved is tiny relative to the complexity cost and the negative aspects of unrecoverable late failure) but for now let's just go with this.
Rich
Re: [PATCH 06/20] signal/sh: Use force_sig(SIGKILL) instead of do_group_exit(SIGKILL)
In reply to Eric W. BiedermanQuoted 11 lines · Eric W. Biederman
Today the sh code allocates memory the first time a process uses the fpu. If that memory allocation fails, kill the affected task with
force_sig(SIGKILL)rather thando_group_exit(SIGKILL).Calling
do_group_exitfrom an exception handler can potentially lead to dead locks asdo_group_exitis not designed to be called from interrupt context. Instead useforce_sig(SIGKILL)to kill the userspace process. Sending signals in general andforce_sigin particular has been tested from interrupt context so there should be no problems.
Quoted 5 lines
Cc Yoshinori Sato <[email protected]>Cc Rich Felker <[email protected]>Fixes 0ea820cf9bf5 ("sh: Move over to dynamically allocated FPU context.")Signed-off-by "Eric W. Biederman" <[email protected]>
Looks sane; there should be no observable changes.
Signature · 1 line
Kees Cook
PATCH 7/20 signal/powerpc: On swapcontext failure force SIGSEGV
In reply to Eric W. BiedermanIf the register state may be partial and corrupted instead of calling do_exit, call force_sigsegv(SIGSEGV). Which properly kills the process with SIGSEGV and does not let any more userspace code execute, instead of just killing one thread of the process and potentially confusing everything.
Cc: Michael Ellerman <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: [email protected]
History-tree: git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
--- arch/powerpc/kernel/signal_32.c | 6 ++++-- arch/powerpc/kernel/signal_64.c | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-)
arch/powerpc/kernel/signal_32.c +4 -2
@@ -1062,8 +1062,10 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,* or if another thread unmaps the region containing the context. * We kill the task with a SIGSEGV in this situation. */ -if (do_setcontext (new_ctx, regs,0 )) -do_exit (SIGSEGV ); +if (do_setcontext (new_ctx, regs,0 )) { +force_sigsegv (SIGSEGV ); +return -EFAULT ; + }set_thread_flag (TIF_RESTOREALL );return 0 ;
arch/powerpc/kernel/signal_64.c +6 -3
@@ -703,15 +703,18 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,* We kill the task with a SIGSEGV in this situation. */ -if (__get_user_sigset (&set, &new_ctx->uc_sigmask)) -do_exit (SIGSEGV ); +if (__get_user_sigset (&set, &new_ctx->uc_sigmask)) { +force_sigsegv (SIGSEGV ); +return -EFAULT ; + }set_current_blocked (&set);if (!user_read_access_begin (new_ctx, ctx_size))return -EFAULT ;if (__unsafe_restore_sigcontext (current,NULL ,0 , &new_ctx->uc_mcontext)) {user_read_access_end (); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); +return -EFAULT ; }user_read_access_end ();
Signature · 1 line
2.20.1
Re: [PATCH 07/20] signal/powerpc: On swapcontext failure force SIGSEGV
Quoted 6 lines · Eric W. Biederman
If the register state may be partial and corrupted instead of calling
do_exit, callforce_sigsegv(SIGSEGV). Which properly kills the process with SIGSEGV and does not let any more userspace code execute, instead of just killing one thread of the process and potentially confusing everything.
Quoted 8 lines
Cc: Michael Ellerman <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: [email protected]
History-tree:git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.gitFixes 756f1ae8a44e ("PPC32: Rework signal code and add a swapcontext system call.")Fixes 04879b04bf50 ("[PATCH] ppc64: VMX (Altivec) support & signal32 rework, from Ben Herrenschmidt")Signed-off-by "Eric W. Biederman" <[email protected]>
This looks right to me.
Signature · 1 line
Kees Cook
PATCH 8/20 signal/sparc: In setup_tsb_params convert open coded BUG into BUG
In reply to Eric W. BiedermanThe function setup_tsb_params has exactly one caller tsb_grow. The function tsb_grow passes in a tsb_bytes value that is between 8192 and 1048576 inclusive, and is guaranteed to be a power of 2. The function setup_tsb_params verifies this property with a switch statement and then prints an error and causes the task to exit if this is not true.
In practice that print statement can never be reached because tsb_grow never passes in a bad tsb_size. So if tsb_size ever gets a bad value that is a kernel bug.
So replace the do_exit which is effectively an open coded version of BUG() with an actuall call to BUG(). Making it clearer that this is a case that can never, and should never happen.
--- arch/sparc/mm/tsb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/sparc/mm/tsb.c +1 -1
@@ -266,7 +266,7 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigndefault :printk (KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n" , current->comm, current->pid, tsb_bytes); -do_exit (SIGSEGV ); +BUG (); } tte |=pte_sz_bits (page_sz);
Signature · 1 line
2.20.1
Re: [PATCH 08/20] signal/sparc: In setup_tsb_params convert open coded BUG into BUG
Quoted 26 lines · Eric W. Biederman
The function
setup_tsb_paramshas exactly one callertsb_grow. The functiontsb_growpasses in atsb_bytesvalue that is between 8192 and 1048576 inclusive, and is guaranteed to be a power of 2. The functionsetup_tsb_paramsverifies this property with a switch statement and then prints an error and causes the task to exit if this is not true.In practice that print statement can never be reached because
tsb_grownever passes in a badtsb_size. So iftsb_sizeever gets a bad value that is a kernel bug.So replace the
do_exitwhich is effectively an open coded version ofBUG()with an actuall call toBUG(). Making it clearer that this is a case that can never, and should never happen.Cc David Miller <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/sparc/mm/tsb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)arch/sparc/mm/tsb.c +0
-0
arch/sparc/mm/tsb.c static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsign · line 271
Quoted 7 lines
arch/sparc/mm/tsb.c +1
-1@@ -266,7 +266,7 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigndefault :printk (KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n" , current->comm, current->pid, tsb_bytes); -do_exit (SIGSEGV ); +BUG (); } tte |=pte_sz_bits (page_sz);
Given the other uses of BUG() here, this seems okay.
Signature · 1 line
Kees Cook
PATCH 9/20 signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
In reply to Eric W. BiedermanThe function save_v86_state is only called when userspace was operating in vm86 mode before entering the kernel. Not having vm86 state in the task_struct should never happen. So transform the hand rolled BUG_ON into an actual BUG_ON to make it clear what is happening.
--- arch/x86/kernel/vm86_32.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
arch/x86/kernel/vm86_32.c +2 -4
@@ -106,10 +106,8 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)*/ local_irq_enable (); -if (!vm86 || !vm86->user_vm86) { -pr_alert ("no user_vm86: BAD\n" ); -do_exit (SIGSEGV ); - } +BUG_ON (!vm86 || !vm86->user_vm86); +set_flags (regs->pt.flags,VEFLAGS ,X86_EFLAGS_VIF | vm86->veflags_mask); user = vm86->user_vm86;
Signature · 1 line
2.20.1
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 5 lines · Eric W. Biederman
The function
save_v86_stateis only called when userspace was operating in vm86 mode before entering the kernel. Not having vm86 state in thetask_structshould never happen. So transform the hand rolledBUG_ONinto an actualBUG_ONto make it clear what is happening.
If this is actually not a state userspace can put itself into:
Otherwise, this should be a WARN+kill.
Quoted 31 lines
Cc Thomas Gleixner <[email protected]>Cc Ingo Molnar <[email protected]>Cc Borislav Petkov <[email protected]>Cc H Peter Anvin <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/x86/kernel/vm86_32.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)arch/x86/kernel/vm86_32.c +2
-4@@ -106,10 +106,8 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)*/ local_irq_enable (); -if (!vm86 || !vm86->user_vm86) { -pr_alert ("no user_vm86: BAD\n" ); -do_exit (SIGSEGV ); - } +BUG_ON (!vm86 || !vm86->user_vm86); +set_flags (regs->pt.flags,VEFLAGS ,X86_EFLAGS_VIF | vm86->veflags_mask); user = vm86->user_vm86;Signature · 1 line
2.20.1
Signature · 1 line
Kees Cook
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
In reply to Eric W. BiedermanQuoted 5 lines · Eric W. Biederman
The function
save_v86_stateis only called when userspace was operating in vm86 mode before entering the kernel. Not having vm86 state in thetask_structshould never happen. So transform the hand rolledBUG_ONinto an actualBUG_ONto make it clear what is happening.
Now that this change has been merged into Linus' tree I have a report that it is possible to trigger this new BUG_ON. Which obviously is not good.
We could revert the change but I think that would just be shooting the messenger.
Does anyone have an idea where to start to track down what is going on?
A very quick skim through the code suggests that the only code path that calls save_v86_state that has not already accessed is current->thread.vm86 is handle_signal.
Another quick look suggests that the only place where X86_VM_MASK gets set in eflags is in do_sys_vm86. So it appears do_sys_vm86 must be called before v8086_mode returns true in handle_signal.
Which seems to suggest that the bug on can't trigger.
But that is obviously wrong.
I will keep digging but if anyone has some ideas that would be appreciated.
Eric
Quoted 26 lines
Cc Thomas Gleixner <[email protected]>Cc Ingo Molnar <[email protected]>Cc Borislav Petkov <[email protected]>Cc H Peter Anvin <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/x86/kernel/vm86_32.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)arch/x86/kernel/vm86_32.c +2
-4@@ -106,10 +106,8 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)*/ local_irq_enable (); -if (!vm86 || !vm86->user_vm86) { -pr_alert ("no user_vm86: BAD\n" ); -do_exit (SIGSEGV ); - } +BUG_ON (!vm86 || !vm86->user_vm86); +set_flags (regs->pt.flags,VEFLAGS ,X86_EFLAGS_VIF | vm86->veflags_mask); user = vm86->user_vm86;
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 26 lines · Eric W. Biederman
Quoted 5 lines · Eric W. Biederman
The function
save_v86_stateis only called when userspace was operating in vm86 mode before entering the kernel. Not having vm86 state in thetask_structshould never happen. So transform the hand rolledBUG_ONinto an actualBUG_ONto make it clear what is happening.Now that this change has been merged into Linus' tree I have a report that it is possible to trigger this new
BUG_ON. Which obviously is not good.We could revert the change but I think that would just be shooting the messenger.
Does anyone have an idea where to start to track down what is going on?
A very quick skim through the code suggests that the only code path that calls
save_v86_statethat has not already accessed iscurrent->thread.vm86ishandle_signal.Another quick look suggests that the only place where
X86_VM_MASKgets set in eflags is indo_sys_vm86. So it appearsdo_sys_vm86must be called beforev8086_modereturns true inhandle_signal.
Quoted 7 lines
Which seems to suggest that the bug on can't trigger.
But that is obviously wrong.
I will keep digging but if anyone has some ideas that would be appreciated.
Eric
It's possible that a null pointer was passed to the vm86 syscall. Since vm86 mode usually requires memory to be mapped at address 0 this wouldn't trigger a fault when reading the vm86_struct data. It should be fine to remove !vm86->user_vm86 from the BUG_ON(), since the write to userspace can handle a fault.
--
Brian Gerst
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 34 lines · Brian Gerst
Quoted 32 lines · Eric W. Biederman
Quoted 5 lines · Eric W. Biederman
The function
save_v86_stateis only called when userspace was operating in vm86 mode before entering the kernel. Not having vm86 state in thetask_structshould never happen. So transform the hand rolledBUG_ONinto an actualBUG_ONto make it clear what is happening.Now that this change has been merged into Linus' tree I have a report that it is possible to trigger this new
BUG_ON. Which obviously is not good.We could revert the change but I think that would just be shooting the messenger.
Does anyone have an idea where to start to track down what is going on?
A very quick skim through the code suggests that the only code path that calls
save_v86_statethat has not already accessed iscurrent->thread.vm86ishandle_signal.Another quick look suggests that the only place where
X86_VM_MASKgets set in eflags is indo_sys_vm86. So it appearsdo_sys_vm86must be called beforev8086_modereturns true inhandle_signal.Which seems to suggest that the bug on can't trigger.
But that is obviously wrong.
I will keep digging but if anyone has some ideas that would be appreciated.
Quoted 7 lines
Quoted 1 line
Eric
It's possible that a null pointer was passed to the vm86 syscall. Since vm86 mode usually requires memory to be mapped at address 0 this wouldn't trigger a fault when reading the
vm86_structdata. It should be fine to remove !vm86->user_vm86 from theBUG_ON(), since the write to userspace can handle a fault.
Agreed, and that is plausible.
Still user space would have had to have mapped address 0 to get that value set in do_sys_vm86.
I will see about cooking up a patch for that case regardless. I am not quite convinced, but perhaps the easiest way to tell is to simply remove the unnecessary test and ask the fuzzer folks to see if they can still hit the BUG_ON.
Eric
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 3 lines · Eric W. Biederman
Still user space would have had to have mapped address 0 to get that value set in
do_sys_vm86.
You have to map address 0 anyway just to get vm86 mode to work.
vm86 mode fundamentally requires the low 1MB of virtual memory to be mapped, since there is no virtual memory offset in the vm86 model.
Linus
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 10 lines · Linus Torvalds
Quoted 3 lines · Eric W. Biederman
Still user space would have had to have mapped address 0 to get that value set in
do_sys_vm86.You have to map address 0 anyway just to get vm86 mode to work.
vm86 mode fundamentally requires the low 1MB of virtual memory to be mapped, since there is no virtual memory offset in the vm86 model.
True.
However that also means if struct vm86plus_struct is at address 0 instead of the 16bit interrupt table something is badly wrong.
Still if we are going to check for userspace being silly that it should be in do_sys_vm86.
I have managed to get the fuzzer that hit the problem to run and with the test for !vm86->user_vm86 removed the BUG_ON is not being hit.
I am going to keep running it for a bit just to make certain, and then I will put together a proper patch.
Eric
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 3 lines · Eric W. Biederman
Still if we are going to check for userspace being silly that it should be in
do_sys_vm86.
Sore, something like
if (!user_vm86)
return -EINVAL;in do_sys_vm86() sounds fine to me.
It could in theory break some odd test-case, but I can't see anybody putting the vm86 save area at 0 in a real situation.
But I could see some quick test hack doing it - the IVT at boot is actually not at zero, but at fffxxxxx. 8086 is magic.
Linus
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
Quoted 3 lines · Linus Torvalds
But I could see some quick test hack doing it - the IVT at boot is actually not at zero, but at fffxxxxx. 8086 is magic.
.. and it's been too long, and I'm too lazy to check - it may be that vm86 mode doesn't even do that magic boot-time address thing.
It's not like we really care about vm86 mode any more, since pretty much nobody users it.
Linus
[GIT PULL ] signal/vm86_32: Remove pointless test in BUG_ON
Linus,
Please pull the exit-cleanups-for-v5.16 branch from the git tree:
git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git exit-cleanups-for-v5.16
HEAD: c7a9b6471c8ee6a2180fc5f2f7a1e284754bdfc5 signal/vm86_32: Remove pointless test in BUG_ON
This branch has only one unpulled change. Just the removal of an unnecessary test from a BUG_ON. Which in my running of the fuzzer locally fixes the issue.
kernel test robot <[email protected]> writes[1]:
Quoted 24 lines
Greeting,
FYI, we noticed the following commit (built with gcc-9):
commit:
1a4d21a23c4ca7467726be7db9ae8077a62b2c62("signal/vm86_32: Replace open codedBUG_ONwith an actualBUG_ON")
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git masterin testcase: trinity
version: trinity-static-i386-x86_64-1c734c75-1_2020-01-06 with following parameters:[ 70.645554][ T3747] kernel BUG at arch/x86/kernel/vm86_32.c:109! [ 70.646185][ T3747] invalid opcode: 0000 [#1] SMP [ 70.646682][ T3747] CPU: 0 PID: 3747 Comm: trinity-c6 Not tainted 5.15.0-rc1-00009-g1a4d21a23c4c #1 [ 70.647598][ T3747] EIP: save_v86_state (arch/x86/kernel/vm86_32.c:109 (discriminator 3)) [ 70.648113][ T3747] Code: 89 c3 64 8b 35 60 b8 25 c2 83 ec 08 89 55 f0 8b 96 10 19 00 00 89 55 ec e8 c6 2d 0c 00 fb 8b 55 ec 85 d2 74 05 83 3a 00 75 02 <0f> 0b 8b 86 10 19 00 00 8b 4b 38 8b 78 48 31 cf 89 f8 8b 7a 4c 81 [ 70.650136][ T3747] EAX: 00000001 EBX: f5f49fac ECX: 0000000b EDX: f610b600 [ 70.650852][ T3747] ESI: f5f79cc0 EDI: f5f79cc0 EBP: f5f49f04 ESP: f5f49ef0 [ 70.651593][ T3747] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 EFLAGS: 00010246 [ 70.652413][ T3747] CR0: 80050033 CR2: 00004000 CR3: 35fc7000 CR4: 000406d0 [ 70.653169][ T3747] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 [ 70.653897][ T3747] DR6: fffe0ff0 DR7: 00000400
Quoted 8 lines
[ 70.654382][ T3747] Call Trace: [ 70.654719][ T3747] arch_do_signal_or_restart (arch/x86/kernel/signal.c:792 arch/x86/kernel/signal.c:867) [ 70.655288][ T3747] exit_to_user_mode_prepare (kernel/entry/common.c:174 kernel/entry/common.c:209) [ 70.655854][ T3747] irqentry_exit_to_user_mode (kernel/entry/common.c:126 kernel/entry/common.c:317) [ 70.656450][ T3747] irqentry_exit (kernel/entry/common.c:406) [ 70.656897][ T3747] exc_page_fault (arch/x86/mm/fault.c:1535) [ 70.657369][ T3747] ? sysvec_kvm_asyncpf_interrupt (arch/x86/mm/fault.c:1488) [ 70.657989][ T3747] handle_exception (arch/x86/entry/entry_32.S:1085)
vm86_32.c:109 is: "BUG_ON(!vm86 || !vm86->user_vm86)"
When trying to understand the failure Brian Gerst pointed out[2] that the code does not need protection against vm86->user_vm86 being NULL. The copy_from_user code will already handles that case if the address is going to fault.
Looking futher I realized that if we care about not allowing struct vm86plus_struct at address 0 it should be do_sys_vm86 (the system call) that does the filtering. Not way down deep when the emulation has completed in save_v86_state.
So let's just remove the silly case of attempting to filter a userspace address with a BUG_ON. Existing userspace can't break and it won't make the kernel any more attackable as the userspace access helpers will handle it, if it isn't a good userspace pointer.
I have run the reproducer the fuzzer gave me before I made this change and it reproduced, and after I made this change and I have not seen the reported failure. So it does looks like this fixes the reported issue.
[1] https://lkml.kernel.org/r/20211112074030.GB19820@xsang-OptiPlex-9020
[2] https://lkml.kernel.org/r/CAMzpN2jkK5sAv-Kg_kVnCEyVySiqeTdUORcC=AdG1gV6r8nUew@mail.gmail.com
--- arch/x86/kernel/vm86_32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/vm86_32.c +1 -1
@@ -106,7 +106,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)*/ local_irq_enable (); -BUG_ON (!vm86 || !vm86->user_vm86); +BUG_ON (!vm86);set_flags (regs->pt.flags,VEFLAGS ,X86_EFLAGS_VIF | vm86->veflags_mask); user = vm86->user_vm86;
Signature · 1 line
2.20.1
Re: [GIT PULL ] signal/vm86_32: Remove pointless test in BUG_ON
The pull request you sent on Fri, 12 Nov 2021 15:37:11 -0600:
The pull request you sent on Fri, 12 Nov 2021 15:37:11 -0600:
Quoted 1 line
git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.gitexit-cleanups-for-v5.16
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/d4fa09e514cdb51fc7a2289c445c44ba0c87117b
Thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/prtracker.html
Re: [PATCH 09/20] signal/vm86_32: Replace open coded BUG_ON with an actual BUG_ON
In reply to Linus TorvaldsQuoted 11 lines · Linus Torvalds
Quoted 3 lines · Linus Torvalds
But I could see some quick test hack doing it - the IVT at boot is actually not at zero, but at fffxxxxx. 8086 is magic.
.. and it's been too long, and I'm too lazy to check - it may be that vm86 mode doesn't even do that magic boot-time address thing.
It's not like we really care about vm86 mode any more, since pretty much nobody users it.
As I recall at boot CS == 0xffff0000 EIP == 0x0000fff0 and the cpu is in 16bit mode. Which means the cpu runs the instructions in the last 16bytes of memory at boot up. Which is just enough for a jump somewhere else. Such as 64K backwards where there is enough space to actual have enough code to do something.
I don't think vm86 even attempts to emulate that behavior as it is only concerned about 16bit only cpus and emulation.
In the nobody cares camp I have just sent you a pull request to remove the ancient (except it wasn't a BUG_ON) and problematic test in the BUG_ON.
I think that is enough to resolve this.
Eric
PATCH 10/20 signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
In reply to Eric W. BiedermanInstead of pretending to send SIGSEGV by calling do_exit(SIGSEGV) call force_sigsegv(SIGSEGV) to force the process to take a SIGSEGV and terminate.
Update handle_signal to return immediately when save_v86_state fails and kills the process. Returning immediately without doing anything except killing the process with SIGSEGV is also what signal_setup_done does when setup_rt_frame fails. Plus it is always ok to return immediately without delivering a signal to a userspace handler when a fatal signal has killed the current process.
--- arch/x86/kernel/signal.c | 6 +++++- arch/x86/kernel/vm86_32.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-)
arch/x86/kernel/signal.c +5 -1
@@ -785,8 +785,12 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)bool stepping, failed;struct fpu *fpu = ¤t->thread.fpu; -if (v8086_mode (regs)) +if (v8086_mode (regs)) {save_v86_state ((struct kernel_vm86_regs *) regs,VM86_SIGNAL ); +/* Has save_v86_state failed and killed the process? */ +if (fatal_signal_pending (current)) +return ; + }/* Are we from a system call? */ if (syscall_get_nr (current, regs) !=-1 ) {
arch/x86/kernel/vm86_32.c +1 -1
@@ -159,7 +159,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); Efault:pr_alert ("could not access userspace vm86 info\n" ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); }static int do_vm86_irq_handling (int subfunction,int irqnumber);
Signature · 1 line
2.20.1
Re: [PATCH 10/20] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 10 lines · Eric W. Biederman
Instead of pretending to send SIGSEGV by calling
do_exit(SIGSEGV)callforce_sigsegv(SIGSEGV)to force the process to take a SIGSEGV and terminate.Update
handle_signalto return immediately whensave_v86_statefails and kills the process. Returning immediately without doing anything except killing the process with SIGSEGV is also whatsignal_setup_donedoes whensetup_rt_framefails. Plus it is always ok to return immediately without delivering a signal to a userspace handler when a fatal signal has killed the current process.
Do the tools/testing/selftests/x86 tests all pass after these changes? I know Andy has a bunch of weird corner cases in there.
Quoted 46 lines
Cc Thomas Gleixner <[email protected]>Cc Ingo Molnar <[email protected]>Cc Borislav Petkov <[email protected]>Cc H Peter Anvin <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/x86/kernel/signal.c | 6 +++++- arch/x86/kernel/vm86_32.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-)arch/x86/kernel/signal.c +5
-1@@ -785,8 +785,12 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)bool stepping, failed;struct fpu *fpu = ¤t->thread.fpu; -if (v8086_mode (regs)) +if (v8086_mode (regs)) {save_v86_state ((struct kernel_vm86_regs *) regs,VM86_SIGNAL ); +/* Has save_v86_state failed and killed the process? */ +if (fatal_signal_pending (current)) +return ; + }/* Are we from a system call? */ if (syscall_get_nr (current, regs) !=-1 ) {arch/x86/kernel/vm86_32.c +1
-1@@ -159,7 +159,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); Efault:pr_alert ("could not access userspace vm86 info\n" ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); }static int do_vm86_irq_handling (int subfunction,int irqnumber);Signature · 1 line
2.20.1
Signature · 1 line
Kees Cook
Re: [PATCH 10/20] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 12 lines · Kees Cook
Quoted 10 lines · Eric W. Biederman
Instead of pretending to send SIGSEGV by calling
do_exit(SIGSEGV)callforce_sigsegv(SIGSEGV)to force the process to take a SIGSEGV and terminate.Update
handle_signalto return immediately whensave_v86_statefails and kills the process. Returning immediately without doing anything except killing the process with SIGSEGV is also whatsignal_setup_donedoes whensetup_rt_framefails. Plus it is always ok to return immediately without delivering a signal to a userspace handler when a fatal signal has killed the current process.
Quoted 2 lines
Do the
tools/testing/selftests/x86tests all pass after these changes? I know Andy has a bunch of weird corner cases in there.
That would require a 32bit userspace wouldn't it?
It is a good idea so I will see if I can dig such a box up, but I unfortunately don't have an up-to-date 32bit box handy, or even an up-to-date box with a 32bit userspace.
It has been about 20 years since I have done much with 32bit x86.
How hard is it to run the tests under tools/testing/selftests/... Last time I tried it was a royal pain. I am hoping it is better this round.
Eric
Re: [PATCH 10/20] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 17 lines · Eric W. Biederman
Quoted 12 lines · Kees Cook
Quoted 10 lines · Eric W. Biederman
Instead of pretending to send SIGSEGV by calling
do_exit(SIGSEGV)callforce_sigsegv(SIGSEGV)to force the process to take a SIGSEGV and terminate.Update
handle_signalto return immediately whensave_v86_statefails and kills the process. Returning immediately without doing anything except killing the process with SIGSEGV is also whatsignal_setup_donedoes whensetup_rt_framefails. Plus it is always ok to return immediately without delivering a signal to a userspace handler when a fatal signal has killed the current process.Quoted 2 lines
Do the
tools/testing/selftests/x86tests all pass after these changes? I know Andy has a bunch of weird corner cases in there.
Quoted 7 lines
That would require a 32bit userspace wouldn't it?
It is a good idea so I will see if I can dig such a box up, but I unfortunately don't have an up-to-date 32bit box handy, or even an up-to-date box with a 32bit userspace.
It has been about 20 years since I have done much with 32bit x86.
I've done recent ia32 testing just under qemu with a 32bit x86 image. Since I've got this set up already, I'll give it a spin...
Quoted 3 lines
How hard is it to run the tests under
tools/testing/selftests/... Last time I tried it was a royal pain. I am hoping it is better this round.
It _is_ a little weird. :P I do it like this, pulled from the larger docs[1]:
# Build host
$ make -C tools/testing/selftests gen_tar TARGETS="x86" FORMAT=.xz $ scp $(find tools/testing/selftests -name kselftests.tar.xz) target:
# Target host
$ mkdir kselftests && cd kselftests $ tar -xaf ../kselftests.tar.xz $ ./run_kselftest.sh
[1] https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html
Signature · 1 line
Kees Cook
Andy Lutomirski
maintainerRe: [PATCH 10/20] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
In reply to Eric W. BiedermanQuoted 3 lines · Eric W. Biederman
Instead of pretending to send SIGSEGV by calling
do_exit(SIGSEGV)callforce_sigsegv(SIGSEGV)to force the process to take a SIGSEGV and terminate.
Why? I realize it's more polite, but is this useful enough to justify the need for testing and potential security impacts?
Quoted 8 lines
Update
handle_signalto return immediately whensave_v86_statefails and kills the process. Returning immediately without doing anything except killing the process with SIGSEGV is also whatsignal_setup_donedoes whensetup_rt_framefails. Plus it is always ok to return immediately without delivering a signal to a userspace handler when a fatal signal has killed the current process.
I can mostly understand the individual sentences, but I don't understand what you're getting it. If a fatal signal has killed the current process and we are guaranteed not to hit the exit-to-usermode path, then, sure, it's safe to return unless we're worried that the core dump code will explode.
But, unless it's fixed elsewhere in your series, force_sigsegv() is itself quite racy, or at least looks racy -- it can race against another thread calling sigaction() and changing the action to something other than SIG_DFL. So it does not appear to actually reliably kill the caller, especially if exposed to a malicious user program.
Quoted 16 lines
Cc Thomas Gleixner <[email protected]>Cc Ingo Molnar <[email protected]>Cc Borislav Petkov <[email protected]>Cc H Peter Anvin <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/x86/kernel/signal.c | 6 +++++- arch/x86/kernel/vm86_32.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-)arch/x86/kernel/signal.c +0
-0
arch/x86/kernel/signal.c handle_signal(struct ksignal *ksig, struct pt_regs *regs) · line 792
Quoted 9 lines
arch/x86/kernel/signal.c +4
-1@@ -785,8 +785,12 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)bool stepping, failed;struct fpu *fpu = ¤t->thread.fpu; -if (v8086_mode (regs)) +if (v8086_mode (regs)) {save_v86_state ((struct kernel_vm86_regs *) regs,VM86_SIGNAL ); +/* Has save_v86_state failed and killed the process? */ +if (fatal_signal_pending (current)) +return ;
This might be an ABI break, or at least it could be if anyone cared about vm86. Imagine this wasn't guarded by if (v8086_mode) and was just if (fatal_signal_pending(current)) return; Then all the other processing gets skipped if a fatal signal is pending (e.g. from a concurrent kill), which could cause visible oddities in a core dump, I think. Maybe it's minor.
Quoted 9 lines
arch/x86/kernel/signal.c +1
-0+ }/* Are we from a system call? */ if (syscall_get_nr (current, regs) !=-1 ) {arch/x86/kernel/vm86_32.c +0
-0
arch/x86/kernel/vm86_32.c void save_v86_state(struct kernel_vm86_regs *regs, · line 163
Quoted 6 lines
arch/x86/kernel/vm86_32.c +1
-1@@ -159,7 +159,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); Efault:pr_alert ("could not access userspace vm86 info\n" ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV );
This causes us to run unwitting kernel code with the vm86 garbage still loaded into the relevant architectural areas (see the chunk if save_v86_state that's inside preempt_disable()). So NAK, especially since the aforementioned race might cause the exit-to-usermode path to actually run with who-knows-what consequences.
If you really want to make this change, please arrange for save_v86_state() to switch out of vm86 mode *before* anything that might fail so that it's guaranteed to at least put the task in a sane state. And write an explicit test case that tests it. I could help with the latter if you do the former.
--Andy
Re: [PATCH 10/20] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 7 lines · Andy Lutomirski
Quoted 3 lines · Eric W. Biederman
Instead of pretending to send SIGSEGV by calling
do_exit(SIGSEGV)callforce_sigsegv(SIGSEGV)to force the process to take a SIGSEGV and terminate.Why? I realize it's more polite, but is this useful enough to justify the need for testing and potential security impacts?
The why is that do_exit as an interface needs to be refactored.
As it exists right now "do_exit" is bad enough that on a couple of older architectures do_exit in a random location results in being able to read/write the kernel stack using ptrace.
So to addresses the issues I need to get everything that really shouldn't be using do_exit to use something else.
Quoted 14 lines
Quoted 7 lines
Update
handle_signalto return immediately whensave_v86_statefails and kills the process. Returning immediately without doing anything except killing the process with SIGSEGV is also whatsignal_setup_donedoes whensetup_rt_framefails. Plus it is always ok to return immediately without delivering a signal to a userspace handler when a fatal signal has killed the current process.I can mostly understand the individual sentences, but I don't understand what you're getting it. If a fatal signal has killed the current process and we are guaranteed not to hit the exit-to-usermode path, then, sure, it's safe to return unless we're worried that the core dump code will explode.
Quoted 6 lines
But, unless it's fixed elsewhere in your series,
force_sigsegv()is itself quite racy, or at least looks racy -- it can race against another thread callingsigaction()and changing the action to something other thanSIG_DFL. So it does not appear to actually reliably kill the caller, especially if exposed to a malicious user program.
You are correct about the races. I have changes in the works to make the races go away but that is not an excuse for push a change that is buggy without them.
Quoted 19 lines
Quoted 19 lines
Cc Thomas Gleixner <[email protected]>Cc Ingo Molnar <[email protected]>Cc Borislav Petkov <[email protected]>Cc H Peter Anvin <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/x86/kernel/signal.c | 6 +++++- arch/x86/kernel/vm86_32.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-)arch/x86/kernel/signal.c +0
-0@@ -785,8 +785,12 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)bool stepping, failed;struct fpu *fpu = ¤t->thread.fpu;
Quoted 13 lines
arch/x86/kernel/signal.c handle_signal(struct ksignal *ksig, struct pt_regs *regs) · line 792
Quoted 6 lines
arch/x86/kernel/signal.c +4
-1-if (v8086_mode (regs)) +if (v8086_mode (regs)) {save_v86_state ((struct kernel_vm86_regs *) regs,VM86_SIGNAL ); +/* Has save_v86_state failed and killed the process? */ +if (fatal_signal_pending (current)) +return ;This might be an ABI break, or at least it could be if anyone cared about vm86. Imagine this wasn't guarded by if (
v8086_mode) and was just if (fatal_signal_pending(current)) return; Then all the other processing gets skipped if a fatal signal is pending (e.g. from a concurrent kill), which could cause visible oddities in a core dump, I think. Maybe it's minor.
I believe it is minor, because the test happens before anything is written to userspace. The worst case is a signal gets dequeued and then not written to userspace.
On a second I am not certain this test is even necessary. Especially if the change you suggest be made to save_v86_state is made so that the kernel is out of v86 state and kernel things can safely happen.
Quoted 13 lines
Quoted 13 lines
arch/x86/kernel/signal.c +1
-0+ }/* Are we from a system call? */ if (syscall_get_nr (current, regs) !=-1 ) {arch/x86/kernel/vm86_32.c +0
-0@@ -159,7 +159,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); Efault:pr_alert ("could not access userspace vm86 info\n" );
Quoted 8 lines
arch/x86/kernel/vm86_32.c void save_v86_state(struct kernel_vm86_regs *regs, int retval) · line 162
Quoted 2 lines
arch/x86/kernel/vm86_32.c +1
-1-do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV );This causes us to run unwitting kernel code with the vm86 garbage still loaded into the relevant architectural areas (see the chunk if
save_v86_statethat's insidepreempt_disable()). So NAK, especially since the aforementioned race might cause the exit-to-usermode path to actually run with who-knows-what consequences.
Fair. I suspect it might even make the current do_exit call run with who-knows-what consequence.
Quoted 5 lines
If you really want to make this change, please arrange for
save_v86_state()to switch out of vm86 mode *before* anything that might fail so that it's guaranteed to at least put the task in a sane state. And write an explicit test case that tests it. I could help with the latter if you do the former.
I do really want to remove this do_exit. If the error was causes by a kernel malfunction we could do something like die.
As it is the code is effectively hand rolling die/oops for a userspace caused condition. Which is quite nasty from a maintenance point of view.
I think your suggested changes to save_v86_state are much more robust than my idea of simply calling force_sig... and expecting the kernel
to exit immediately. Having to go another pass through theexit_to_usermode_loop does not look like it is very hard to make it robust against a kernel in a random state.
I could close the race today by replacing the force_sigsegv(SIGSEGV) with force_sig(SIGKILL). And that removes the coredump path from the equation so is a bit interesting, but it really is unsatisfactory.
I will dig in and see what can be done including writing a test so that this code path gracefully handles -EFAULT rather than tries to walk through the rest of the kernel in a problematic state.
This change as proposed does not get this save_v86_state case to using ordinary mechanisms to handle the problem, so as written it does not solve the problem it set out to solve.
Eric
A message that is not in the archive (sent off-list or not yet received): 875ytkygfj.fsf_-_@disp2133
Re: [PATCH v2 10/32] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 5 lines · Eric W. Biederman
Update
save_v86_stateto always complete all of it's work except possibly some of the copies to userspace even ifsave_v86_statetakes a fault. This ensures that the kernel is always in a sane state, even if userspace has done something silly.
Well, honestly, with this change, you might as well replace the force_sigsegv() with just a plain "force_sig()", and make it something the process can catch.
The only thing that "force_sigsgv()" does is to make SIGSEGV uncatchable. In contrast, a plain "force_sig()" just means that it can't be ignored - but it can be caught, and it is fatal only when not caught.
And with the "always complete the non-vm86 state restore" part change, there's really no reason for it to not be caught.
Of course, the other case (where we have no state information for the "enter vm86 mode" case) is still fatal, and is a "this should never happen". But the "cannot write to the vm86 save state" thing isn't technically fatal.
It should even be possible to write a test for it: passing a read-only pointer to the vm86() system call. The vm86 entry will work (because it only reads the vm86 state from it), but then at vm86 exit, writing the state back will fail.
Anybody?
Linus
Re: [PATCH v2 10/32] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 10 lines · Linus Torvalds
Quoted 5 lines · Eric W. Biederman
Update
save_v86_stateto always complete all of it's work except possibly some of the copies to userspace even ifsave_v86_statetakes a fault. This ensures that the kernel is always in a sane state, even if userspace has done something silly.Well, honestly, with this change, you might as well replace the
force_sigsegv()with just a plain "force_sig()", and make it something the process can catch.
The trouble is I don't think there is enough information made available for user space to do anything with the SIGSEGV. My memory is that applications like dosemu very much have a SIGSEGV handler.
So I think if it ever happened it could be quite confusing. Not to mention the pr_alert message.
But I guess if a test is written like you suggest we can include enough information for someone to make sense of things.
Quoted 13 lines
The only thing that "
force_sigsgv()" does is to make SIGSEGV uncatchable. In contrast, a plain "force_sig()" just means that it can't be ignored - but it can be caught, and it is fatal only when not caught.And with the "always complete the non-vm86 state restore" part change, there's really no reason for it to not be caught.
Of course, the other case (where we have no state information for the "enter vm86 mode" case) is still fatal, and is a "this should never happen". But the "cannot write to the vm86 save state" thing isn't technically fatal.
Quoted 6 lines
It should even be possible to write a test for it: passing a read-only pointer to the
vm86()system call. The vm86 entry will work (because it only reads the vm86 state from it), but then at vm86 exit, writing the state back will fail.Anybody?
I am enthusiastic about writing a test, but I will plod in that direction just so I can get this sorted out.
Eric
Andy Lutomirski
maintainerRe: [PATCH v2 10/32] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
In reply to a message not in the archiveQuoted 35 lines · Eric W. Biederman
Update
save_v86_stateto always complete all of it's work except possibly some of the copies to userspace even ifsave_v86_statetakes a fault. This ensures that the kernel is always in a sane state, even if userspace has done something silly.When
save_v86_statetakes a fault update it to force userspace to take a SIGSEGV and terminate the userspace application.As Andy pointed out in review of the first version of this change there are races between sigaction and the application terinating. Now that the code has been modified to always perform all save_v86_state's work (except possibly copying to userspace) those races do not matter from a kernel perspective.
Forcing the userspace application to terminate (by resetting it's handler to SIGDFL) is there to keep everything as close to the current behavior as possible while removing the unique (and difficult to maintain) use of
do_exit.If this new SIGSEGV happens during
handle_signalthe next time around theexit_to_user_mode_loop, SIGSEGV will be delivered to userspace.All of the callers of
handle_vm86_trapandhandle_vm86_faultrun theexit_to_user_mode_loopbefore they return to userspace any signal sent to the current task during their execution will be delivered to the current task before that tasks exits to usermode.Cc: Andy Lutomirski <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: [email protected]
Cc: H Peter Anvin <[email protected]>
v1: https://lkml.kernel.org/r/[email protected]
Quoted 6 lines
Signed-off-by Eric W. Biederman <[email protected]>--- arch/x86/kernel/vm86_32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)Any does this look better?
Conceptually yes, but:
Quoted 18 lines
I think by just completing all of the work that isn't copying to userspace this makes
save_v86_statemuch more robust.arch/x86/kernel/vm86_32.c +1
-0@@ -140,6 +140,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); +exit_vm86:preempt_disable (); tsk->thread.sp0 = vm86->saved_sp0; tsk->thread.sysenter_cs = __KERNEL_CS;
arch/x86/kernel/vm86_32.c void save_v86_state(struct kernel_vm86_regs *regs, int retval) · line 167
Quoted 9 lines
arch/x86/kernel/vm86_32.c +2
-1@@ -159,7 +160,8 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); Efault:pr_alert ("could not access userspace vm86 info\n" ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); + goto exit_vm86; }static int do_vm86_irq_handling (int subfunction,int irqnumber);
I think the result would be nicer if, instead of adding an extra goto, you just literally moved all the cleanup under the unsafe_put_user()s above them. Unless I missed something, none of the put_user stuff reads any state that is written by the cleanup code.
--Andy
Re: [PATCH v2 10/32] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 5 lines · Andy Lutomirski
I think the result would be nicer if, instead of adding an extra goto, you just literally moved all the cleanup under the unsafe_put_user()s above them. Unless I missed something, none of the
put_userstuff reads any state that is written by the cleanup code.
Sure it does:
memcpy(®s->pt, &vm86->regs32, sizeof(struct pt_regs));
is very much part of the cleanup code, and overwrites that regs->pt thing.
Which is exactly what we're writing back to user space in that unsafe_put_user() thing.
That said, thinking more about this, and looking at it again, I take back my statement that we could just make it a catchable SIGSEGV instead.
If we can't write the vm86 state to user space, we will have fundamentally lost it, and while it's not fatal to the kernel, and while we've recovered the original 32-bit state, it's not something that user space can sanely recover from because the register state at the end of the vm86 work has now been irrecoverably thrown away.
So I think Eric's patch is fine.
Except, as mentioned as part of the other patch, the "force_sigsegv()" conversion to use "force_fatal_sig()" was broken, because that function wasn't actually fatal at all.
Linus
Andy Lutomirski
maintainerRe: [PATCH v2 10/32] signal/vm86_32: Properly send SIGSEGV when the vm86 state cannot be saved.
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Andy Lutomirski
I think the result would be nicer if, instead of adding an extra goto, you just literally moved all the cleanup under the unsafe_put_user()s above them. Unless I missed something, none of the
put_userstuff reads any state that is written by the cleanup code.
Quoted 8 lines
Sure it does:
memcpy(®s->pt, &vm86->regs32, sizeof(struct pt_regs));is very much part of the cleanup code, and overwrites that
regs->ptthing.Which is exactly what we're writing back to user space in that
unsafe_put_user()thing.
D’oh, right.
Quoted 10 lines
That said, thinking more about this, and looking at it again, I take back my statement that we could just make it a catchable SIGSEGV instead.
If we can't write the vm86 state to user space, we will have fundamentally lost it, and while it's not fatal to the kernel, and while we've recovered the original 32-bit state, it's not something that user space can sanely recover from because the register state at the end of the vm86 work has now been irrecoverably thrown away.
There’s “recoverable” and there’s “recoverable”. Sure, the vm86 state is gone, but the process is getting a signal that doesn’t indicate that one can freely return and carry on as if nothing happened. But one can catch the signal and go on to do something else.
Quoted 2 lines
So I think Eric's patch is fine.
Me too.
Quoted 6 lines
Except, as mentioned as part of the other patch, the "
force_sigsegv()" conversion to use "force_fatal_sig()" was broken, because that function wasn't actually fatal at all.Linus
PATCH 11/20 signal/s390: Use force_sigsegv in default_trap_handler
In reply to Eric W. BiedermanReading the history it is unclear why default_trap_handler calls do_exit. It is not even menthioned in the commit where the change happened. My best guess is that because it is unknown why the exception happened it was desired to guarantee the process never returned to userspace.
Using do_exit(SIGSEGV) has the problem that it will only terminate one thread of a process, leaving the process in an undefined state.
Use force_sigsegv(SIGSEGV) instead which effectively has the same behavior except that is uses the ordinary signal mechanism and terminates all threads of a process and is generally well defined.
History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
--- arch/s390/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/kernel/traps.c +1 -1
@@ -84,7 +84,7 @@ static void default_trap_handler(struct pt_regs *regs) {if (user_mode (regs)) {report_user_fault (regs,SIGSEGV ,0 ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); }else die (regs,"Unknown program exception" ); }
Signature · 1 line
2.20.1
Re: [PATCH 11/20] signal/s390: Use force_sigsegv in default_trap_handler
Quoted 13 lines · Eric W. Biederman
Reading the history it is unclear why
default_trap_handlercallsdo_exit. It is not even menthioned in the commit where the change happened. My best guess is that because it is unknown why the exception happened it was desired to guarantee the process never returned to userspace.Using
do_exit(SIGSEGV)has the problem that it will only terminate one thread of a process, leaving the process in an undefined state.Use
force_sigsegv(SIGSEGV)instead which effectively has the same behavior except that is uses the ordinary signal mechanism and terminates all threads of a process and is generally well defined.
Quoted 7 lines
Cc Heiko Carstens <[email protected]>Cc Vasily Gorbik <[email protected]>Cc Christian Borntraeger <[email protected]>Fixes ca2ab03237ec ("[PATCH] s390: core changes")History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
Re: [PATCH 11/20] signal/s390: Use force_sigsegv in default_trap_handler
In reply to Eric W. BiedermanAm 20.10.21 um 19:43 schrieb Eric W. Biederman:
Quoted 6 lines
Reading the history it is unclear why
default_trap_handlercallsdo_exit. It is not even menthioned in the commit where the change happened. My best guess is that because it is unknown why the exception happened it was desired to guarantee the process never returned to userspace.
Quoted 6 lines
Using
do_exit(SIGSEGV)has the problem that it will only terminate one thread of a process, leaving the process in an undefined state.Use
force_sigsegv(SIGSEGV)instead which effectively has the same behavior except that is uses the ordinary signal mechanism and terminates all threads of a process and is generally well defined.
Do I get that right, that programs can not block SIGSEGV from force_sigsegv with a signal handler? Thats how I read the code. If this is true then
Quoted 26 lines
Cc Heiko Carstens <[email protected]>Cc Vasily Gorbik <[email protected]>Cc Christian Borntraeger <[email protected]>Fixes ca2ab03237ec ("[PATCH] s390: core changes")History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/s390/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)arch/s390/kernel/traps.c +1
-1@@ -84,7 +84,7 @@ static void default_trap_handler(struct pt_regs *regs) {if (user_mode (regs)) {report_user_fault (regs,SIGSEGV ,0 ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); }else die (regs,"Unknown program exception" ); }
Re: [PATCH 11/20] signal/s390: Use force_sigsegv in default_trap_handler
Quoted 14 lines · Christian Borntraeger
Am 20.10.21 um 19:43 schrieb Eric W. Biederman:
Quoted 6 lines
Reading the history it is unclear why
default_trap_handlercallsdo_exit. It is not even menthioned in the commit where the change happened. My best guess is that because it is unknown why the exception happened it was desired to guarantee the process never returned to userspace.Quoted 6 lines
Using
do_exit(SIGSEGV)has the problem that it will only terminate one thread of a process, leaving the process in an undefined state.Use
force_sigsegv(SIGSEGV)instead which effectively has the same behavior except that is uses the ordinary signal mechanism and terminates all threads of a process and is generally well defined.
Quoted 5 lines
Do I get that right, that programs can not block SIGSEGV from
force_sigsegvwith a signal handler? Thats how I read the code. If this is true thenReviewed-by Christian Borntraeger <[email protected]>
99% true, and it is what force_sigsegv(SIGSEGV) intends to do.
Andy Lutormorski pointed at a race where a thread can call sigaction and change the signal handler after force_sigsegv has run but before the process dequeues the SIGSEGV.
In principle it isn't too hard to close that race, and I was hoping to be able to tell you that I had sorted by the time I replied. Unfortunately it looks like it will take another week or two so will probably not be ready by the merge window.
I am definitely going to close that race.
Eric
Quoted 25 lines
Quoted 25 lines
Cc Heiko Carstens <[email protected]>Cc Vasily Gorbik <[email protected]>Cc Christian Borntraeger <[email protected]>Fixes ca2ab03237ec ("[PATCH] s390: core changes")History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/s390/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)arch/s390/kernel/traps.c +1
-1@@ -84,7 +84,7 @@ static void default_trap_handler(struct pt_regs *regs) {if (user_mode (regs)) {report_user_fault (regs,SIGSEGV ,0 ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); }else die (regs,"Unknown program exception" ); }
Re: [PATCH 11/20] signal/s390: Use force_sigsegv in default_trap_handler
Quoted 20 lines · [email protected] (Eric W. Biederman) writes:
Quoted 18 lines · Christian Borntraeger
Am 20.10.21 um 19:43 schrieb Eric W. Biederman:
Quoted 6 lines
Reading the history it is unclear why
default_trap_handlercallsdo_exit. It is not even menthioned in the commit where the change happened. My best guess is that because it is unknown why the exception happened it was desired to guarantee the process never returned to userspace.Quoted 6 lines
Using
do_exit(SIGSEGV)has the problem that it will only terminate one thread of a process, leaving the process in an undefined state.Use
force_sigsegv(SIGSEGV)instead which effectively has the same behavior except that is uses the ordinary signal mechanism and terminates all threads of a process and is generally well defined.Do I get that right, that programs can not block SIGSEGV from
force_sigsegvwith a signal handler? Thats how I read the code. If this is true then
Quoted 7 lines
Quoted 1 line
Reviewed-by Christian Borntraeger <[email protected]>99% true, and it is what
force_sigsegv(SIGSEGV)intends to do.Andy Lutormorski pointed at a race where a thread can call sigaction and change the signal handler after
force_sigsegvhas run but before the process dequeues the SIGSEGV.
I now have a simple patch that closes the sigaction vs force_sig race, that I am adding to this set of changes. So now I can say programs can not block force_sigsegv(SIGSEGV) with a signal handler or any other method.
Eric
Quoted 25 lines
Quoted 25 lines
Quoted 25 lines
Cc Heiko Carstens <[email protected]>Cc Vasily Gorbik <[email protected]>Cc Christian Borntraeger <[email protected]>Fixes ca2ab03237ec ("[PATCH] s390: core changes")History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/s390/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)arch/s390/kernel/traps.c +1
-1@@ -84,7 +84,7 @@ static void default_trap_handler(struct pt_regs *regs) {if (user_mode (regs)) {report_user_fault (regs,SIGSEGV ,0 ); -do_exit (SIGSEGV ); +force_sigsegv (SIGSEGV ); }else die (regs,"Unknown program exception" ); }
PATCH 12/20 exit/kthread: Have kernel threads return instead of calling do_exit
In reply to Eric W. BiedermanIn 2009 Oleg reworked[1] the kernel threads so that it is not necessary to call do_exit if you are not using kthread_stop(). Remove the explicit calls of do_exit and complete_and_exit (with a NULL completion) that were previously necessary.
[1] 63706172f332 ("kthreads: rework kthread_stop()")
--- drivers/firmware/stratix10-svc.c | 4 ++-- drivers/soc/ti/wkup_m3_ipc.c | 2 +- fs/ocfs2/journal.c | 5 +---- kernel/kthread.c | 2 +- net/batman-adv/tp_meter.c | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-)
drivers/firmware/stratix10-svc.c +2 -2
@@ -520,7 +520,7 @@ static int svc_normal_to_secure_thread(void *data)* physical address of memory block reserved by secure monitor software at * secure world. * -* svc_normal_to_secure_shm_thread() calls do_exit() directly since it is a +* svc_normal_to_secure_shm_thread() terminates directly since it is a * standlone thread for which no one will call kthread_stop() or return when * 'kthread_should_stop()' is true. */
@@ -544,7 +544,7 @@ static int svc_normal_to_secure_shm_thread(void *data) }complete (&sh_mem->sync_complete); -do_exit (0 ); +return 0 ; }/**
drivers/soc/ti/wkup_m3_ipc.c +1 -1
@@ -426,7 +426,7 @@ static void wkup_m3_rproc_boot_thread(struct wkup_m3_ipc *m3_ipc)else m3_ipc_state = m3_ipc; -do_exit (0 ); +return 0 ; }static int wkup_m3_ipc_probe (struct platform_device *pdev)
fs/ocfs2/journal.c +1 -4
@@ -1497,10 +1497,7 @@ static int __ocfs2_recovery_thread(void *arg)if (quota_enabled)kfree (rm_quota); -/* no one is callint kthread_stop() for us so the kthread() api -* requires that we call do_exit(). And it isn't exported, but -* complete_and_exit() seems to be a minimal wrapper around it. */ -complete_and_exit (NULL , status); +return status; }void ocfs2_recovery_thread (struct ocfs2_super *osb,int node_num)
kernel/kthread.c +1 -1
@@ -433,7 +433,7 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),* If thread is going to be bound on a particular cpu, give its node * in @node, to get NUMA affinity for kthread stack, or else give NUMA_NO_NODE. * When woken, the thread will run @threadfn() with @data as its -* argument. @threadfn() can either call do_exit() directly if it is a +* argument. @threadfn() can either return directly if it is a * standalone thread for which no one will call kthread_stop(), or * return when 'kthread_should_stop()' is true (which means * kthread_stop() has been called). The return value should be zero
net/batman-adv/tp_meter.c +1 -1
@@ -890,7 +890,7 @@ static int batadv_tp_send(void *arg)batadv_tp_vars_put (tp_vars); -do_exit (0 ); +return 0 ; }/**
Signature · 1 line
2.20.1
Re: [PATCH 12/20] exit/kthread: Have kernel threads return instead of calling do_exit
Quoted 4 lines · Eric W. Biederman
In 2009 Oleg reworked[1] the kernel threads so that it is not necessary to call
do_exitif you are not usingkthread_stop(). Remove the explicit calls ofdo_exitandcomplete_and_exit(with a NULL completion) that were previously necessary.
With this we should also be able to drop the export for do_exit.
Re: [PATCH 12/20] exit/kthread: Have kernel threads return instead of calling do_exit
Quoted 7 lines · Christoph Hellwig
Quoted 4 lines · Eric W. Biederman
In 2009 Oleg reworked[1] the kernel threads so that it is not necessary to call
do_exitif you are not usingkthread_stop(). Remove the explicit calls ofdo_exitandcomplete_and_exit(with a NULL completion) that were previously necessary.With this we should also be able to drop the export for
do_exit.
Good point.
After this set of changes I don't see any calls of do_exit in drivers or other awkward places so that would make a good addition.
Eric
Re: [PATCH 12/20] exit/kthread: Have kernel threads return instead of calling do_exit
In reply to Eric W. BiedermanQuoted 7 lines · Eric W. Biederman
In 2009 Oleg reworked[1] the kernel threads so that it is not necessary to call
do_exitif you are not usingkthread_stop(). Remove the explicit calls ofdo_exitandcomplete_and_exit(with a NULL completion) that were previously necessary.[1]
63706172f332("kthreads: reworkkthread_stop()")Signed-off-by "Eric W. Biederman" <[email protected]>
Looks sensible. Can you check that tools/testing/selftests/firmware still passes? That test does a fair bit of kthread waiting, etc.
Signature · 1 line
Kees Cook
PATCH 13/20 signal: Implement force_fatal_sig
In reply to Eric W. BiedermanAdd a simple helper force_fatal_sig that causes a signal to be delivered to a process as if the signal handler was set to SIG_DFL.
Reimplement force_sigsegv based upon this new helper. This fixes force_sigsegv so that when it forces the default signal handler to be used the code now forces the signal to be unblocked as well.
Reusing the tested logic in force_sig_info_to_task that was built for force_sig_seccomp this makes the implementation trivial.
This is interesting both because it makes force_sigsegv simpler and because there are a couple of buggy places in the kernel that call do_exit(SIGILL) or do_exit(SIGSYS) because there is no straight forward way today for those places to simply force the exit of a process with the chosen signal. Creating force_fatal_sig allows those places to be implemented with normal signal exits.
--- include/linux/sched/signal.h | 1 + kernel/signal.c | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-)
include/linux/sched/signal.h +1 -0
@@ -338,6 +338,7 @@ extern int kill_pid(struct pid *pid, int sig, int priv);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 int send_sig (int ,struct task_struct *,int );extern int zap_other_threads (struct task_struct *p);extern struct sigqueue *sigqueue_alloc (void );
kernel/signal.c +17 -9
@@ -1662,6 +1662,19 @@ void force_sig(int sig) }EXPORT_SYMBOL (force_sig); +void force_fatal_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, true); +} +/* * When things go south during signal handling, we * will force a SIGSEGV. And if the signal that caused
@@ -1670,15 +1683,10 @@ EXPORT_SYMBOL(force_sig);*/ void force_sigsegv (int sig) { -struct task_struct *p = current; - -if (sig ==SIGSEGV ) { -unsigned long flags; -spin_lock_irqsave (&p->sighand->siglock, flags); - p->sighand->action[sig -1 ].sa.sa_handler =SIG_DFL ; -spin_unlock_irqrestore (&p->sighand->siglock, flags); - } -force_sig (SIGSEGV ); +if (sig ==SIGSEGV ) +force_fatal_sig (SIGSEGV ); +else +force_sig (SIGSEGV ); } int force_sig_fault_to_task(int sig, int code, void __user *addr
Signature · 1 line
2.20.1
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 5 lines · Eric W. Biederman
Add a simple helper
force_fatal_sigthat causes a signal to be delivered to a process as if the signal handler was set toSIG_DFL.Reimplement
force_sigsegvbased upon this new helper.
Can you just make the old force_sigsegv() go away? The odd special casing of SIGSEGV was odd to begin with, I think everybody really just wanted this new "force_fatal_sig()" and allow any signal - not making SIGSEGV special.
Also, I think it should set SIGKILL in p->pending.signal or something like that - because we want this to trigger fatal_signal_pending(), don't we?
Right now fatal_signal_pending() is only true for SIGKILL, I think.
Linus
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 11 lines · Linus Torvalds
Quoted 5 lines · Eric W. Biederman
Add a simple helper
force_fatal_sigthat causes a signal to be delivered to a process as if the signal handler was set toSIG_DFL.Reimplement
force_sigsegvbased upon this new helper.Can you just make the old
force_sigsegv()go away? The odd special casing of SIGSEGV was odd to begin with, I think everybody really just wanted this new "force_fatal_sig()" and allow any signal - not making SIGSEGV special.
There remains the original case that is signal_set up_done deals with generically. When sending a signal fails the code attempts send SIGSEGV and if sending SIGSEGV fails the signal delivery code terminates the process with SIGSEGV.
To keep dependencies to a minimum and to allow for the possibility of backports I used "force_sigsegv(SIGSEGV)" instead of "force_fatal_sig(SIGSEGV)". I will be happy to add an additional patch that converts all of those case to force_fatal_sig.
Quoted 5 lines
Also, I think it should set SIGKILL in
p->pending.signalor something like that - because we want this to triggerfatal_signal_pending(), don't we?Right now
fatal_signal_pending()is only true for SIGKILL, I think.
In general when a fatal signal is delivered the function complete_signal individually delivers SIGKILL to the threads, making fatal_signal_pending true.
For signals like SIGSYS that generate a coredump that is not currently true, but in the cases I looked at signal_pending() was enough to get the code to get_signal(), which dequeues the signals and starts processing them.
I have a branch queued up for the next merge window that implements per signal_struct coredumps. Assuming that does not trigger any user space regressions I can remove the coredump special case in complete_signal. That will in turn mean that force_siginfo_to_task does not need to change sa_handler, blocked or clear SIGNAL_UNKILLABLE, as all of the cases where that matters today will just wind up with complete_signal setting a per_thread SIGKILL.
I keep playing with the idea of having fatal_signal_pending depend on a different flag than the per thread bit for SIGKILL in the per thread signal set. That might make it clearer that complete_signal has started killing the process and it is a start of the killing the process that triggers fatal_signal_pending.
So far the way fatal_signal_pending works hasn't really been a problem so I keep putting away ideas of cleaner implementations.
Eric
Andy Lutomirski
maintainerRe: [PATCH 13/20] signal: Implement force_fatal_sig
In reply to Linus TorvaldsQuoted 5 lines · Linus Torvalds
Quoted 4 lines · Eric W. Biederman
Add a simple helper
force_fatal_sigthat causes a signal to be delivered to a process as if the signal handler was set toSIG_DFL.
Quoted 7 lines
Quoted 1 line
Reimplement
force_sigsegvbased upon this new helper.Can you just make the old
force_sigsegv()go away? The odd special casing of SIGSEGV was odd to begin with, I think everybody really just wanted this new "force_fatal_sig()" and allow any signal - not making SIGSEGV special.
I'm rather nervous about all this, and I'm also nervous about the existing code. A quick skim is finding plenty of code paths that assume force_sigsegv (or a do_exit that this series touches) are genuinely unrecoverable. For example:
- rseq: the *kernel* will be fine if a signal is handled, but the userspace process may be in a very strange state.
- bprm_execve: The comment says it best:
/*
* If past the point of no return ensure the code never
* returns to the userspace process. Use an existing fatal
* signal if present otherwise terminate the process with
* SIGSEGV.
*/
if (bprm->point_of_no_return && !fatal_signal_pending(current))
force_sigsegv(SIGSEGV);
- vm86: already discussed
Now force_sigsegv() at least tries to kill the task, but not very well. With the whole series applied and force_sigsegv() gone, these errors become handleable, and that needs real care.
(I don't think bprm_execve() is exploitable. It looks like it's attackable in the window between setting point_of_no_return and unshare_sighand(), but I'm not seeing any useful way to attack it unless a core dump is already in progress or a *different* fatal signal is already pending, and in either of those cases we're fine.)
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 5 lines · Andy Lutomirski
I'm rather nervous about all this, and I'm also nervous about the existing code. A quick skim is finding plenty of code paths that assume
force_sigsegv(or ado_exitthat this series touches) are genuinely unrecoverable.
I was going to say "what are you talking about", because clearly Eric kept it all fatal.
But then looked at that patch a bit more before I claimed you were wrong.
And yeah, Eric's force_fatal_sig() is completely broken.
It claims to force a fatal signal, but doesn't actually do that at all, and is completely misnamed.
It just uses "force_sig_info_to_task()", which still allows user space to catch signals - so it's not "fatal" in the least. It only punches through SIG_IGN and blocked signals.
So yeah, that's broken.
I do still think that that could the behavior we possibly want for that "can't write updated vm86 state back" situation, but for something that is called "fatal", it really needs to be fatal.
Linus
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 21 lines · Linus Torvalds
Quoted 5 lines · Andy Lutomirski
I'm rather nervous about all this, and I'm also nervous about the existing code. A quick skim is finding plenty of code paths that assume
force_sigsegv(or ado_exitthat this series touches) are genuinely unrecoverable.I was going to say "what are you talking about", because clearly Eric kept it all fatal.
But then looked at that patch a bit more before I claimed you were wrong.
And yeah, Eric's
force_fatal_sig()is completely broken.It claims to force a fatal signal, but doesn't actually do that at all, and is completely misnamed.
It just uses "
force_sig_info_to_task()", which still allows user space to catch signals - so it's not "fatal" in the least. It only punches throughSIG_IGNand blocked signals.
Quoted 5 lines
So yeah, that's broken.
I do still think that that could the behavior we possibly want for that "can't write updated vm86 state back" situation, but for something that is called "fatal", it really needs to be fatal.
Once the code gets as far as force_sig_info_to_task the only bit that is really missing is to make the signals fatal is:
kernel/signal.c +0 -2
@@ -1048,7 +1048,6 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type)/* * This signal will be fatal to the whole group. */ -if (!sig_kernel_coredump (sig)) {/* * Start a group exit and wake everybody up. * This way we don't have other threads
@@ -1065,7 +1064,6 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type)signal_wake_up (t,1 ); }while_each_thread (p, t);return ; - } }/*
AKA the only real bit missing is the interaction with the coredump code.
Now we can't just delete sig_kernel_coredump a replacement has to be
written. And the easiest replacement depends on my other set of
changes that are already in linux-next to make coredumps per signal_struct instead of per mm.
Which means that in a release or two force_fatal_sig will reliably do what the name says.
So the question is: Should I name force_fatal_sig to something else in the meantime? What should I name it?
I do intend to fix that bit in complete_signal, as well as updating the code in force_siginfo_to_task so that it doesn't need to change the blocked state or the signal handler.
These special cases have been annoying me for years and now Andy has found how they are actually hurting us. So I do intend to fix that code as quickly as being careful and code review allows. Which I think means one additional development cycle after this one.
Eric
Re: [PATCH 13/20] signal: Implement force_fatal_sig
In reply to Linus TorvaldsQuoted 12 lines · Linus Torvalds
Quoted 5 lines · Andy Lutomirski
I'm rather nervous about all this, and I'm also nervous about the existing code. A quick skim is finding plenty of code paths that assume
force_sigsegv(or ado_exitthat this series touches) are genuinely unrecoverable.I was going to say "what are you talking about", because clearly Eric kept it all fatal.
But then looked at that patch a bit more before I claimed you were wrong.
Quoted 8 lines
And yeah, Eric's
force_fatal_sig()is completely broken.It claims to force a fatal signal, but doesn't actually do that at all, and is completely misnamed.
It just uses "
force_sig_info_to_task()", which still allows user space to catch signals - so it's not "fatal" in the least. It only punches throughSIG_IGNand blocked signals.
Rereading this I think you might be misreading something.
force_siginfo_to_task takes a sigdfl parameter which I am setting in force_fatal_signal.
When that sigdfl paramter is set force_siginfo_to_task always changes the signal handler to SIGDFL, and always unblocks the signal.
Because the siglock remains held over send_signal none of those properties can change during send_signal. Which means that as long as we are not talking about a coredump signal complete_signal is guaranteed to recognize the signal as fatal immediately.
For coredump signals there is a race where siglock is dropped before get_signal is called that could result in the signal handler being changed or the signal being blocked. Which is why I pointed out the problem is coredumps.
But assuming userspace does not change something in that narrow window the signal will most definitely be fatal to the target process.
Just as soon as I know if we can have per signal_struct coredumps without causing regressions I will close the final race. I can do it either way but the code is much less complicated with per signal_struct coredumps.
Hoisting the current zap_threads from fs/coredump.c into complete_signal is a pain and a half. While just the per_signal struct part is already there, and the code just needs a few tweaks to allow get_signal to act as the coredump rendezvous location.
Eric
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 2 lines · Eric W. Biederman
Rereading this I think you might be misreading something.
Gaah. Yes, indeed.
Quoted 2 lines
force_siginfo_to_tasktakes a sigdfl parameter which I am setting inforce_fatal_signal.
.. and I realized that the first time I read through it, but then when I read through it due to Andy saying it worries him, I missed it and thought the handler didn't get reset.
So the patch is fine.
Linus
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 6 lines · Linus Torvalds
Quoted 2 lines · Eric W. Biederman
Rereading this I think you might be misreading something.
Gaah. Yes, indeed.
Quoted 8 lines
Quoted 2 lines
force_siginfo_to_tasktakes a sigdfl parameter which I am setting inforce_fatal_signal... and I realized that the first time I read through it, but then when I read through it due to Andy saying it worries him, I missed it and thought the handler didn't get reset.
So the patch is fine.
Thank you.
Andy is right that there is a race with sigaction changing the signal handler.
To make complete_signal reliably recognize fatal signals is going to take a bit of work. None of it particularly hard but there are a lot of pieces that need to be changed carefully. Part of the problem is that recognizing fatal signals early started out as an optimization, and there remain places in complete_signal and prepare_signal that assume it is always possible to not recognize fatal signals early and let get_signal handle things.
The coredump handling of it is the biggest challenge. Computing a destination thread with wants_signal in complete_signal before dealing with fatal signals (which don't care) means that wants_signal can cause the early recognition of fatal signals to fail.
The entire blocked vs real_blocked set of signal masks are interesting. The current way those signal masks work does not appear to allow knowing when blocked is being overridden and blocked is stored in real_blocked. Something I think needs to be known if fatal signal are always going to be recognized early.
Given that race it looks like it is important to make the guarantee that fatal signals are always recognized early. So the rest of the kernel can count on that property.
But I aim to get this set of changes into linux-next first. As I don't anything will melt down if we leave that race for a bit longer.
Eric
Re: [PATCH 13/20] signal: Implement force_fatal_sig
In reply to Eric W. BiedermanQuoted 10 lines · Eric W. Biederman
Add a simple helper
force_fatal_sigthat causes a signal to be delivered to a process as if the signal handler was set toSIG_DFL.Reimplement
force_sigsegvbased upon this new helper. This fixesforce_sigsegvso that when it forces the default signal handler to be used the code now forces the signal to be unblocked as well.Reusing the tested logic in
force_sig_info_to_taskthat was built forforce_sig_seccompthis makes the implementation trivial.
Quoted 6 lines
This is interesting both because it makes
force_sigsegvsimpler and because there are a couple of buggy places in the kernel that calldo_exit(SIGILL)ordo_exit(SIGSYS)because there is no straight forward way today for those places to simply force the exit of a process with the chosen signal. Creatingforce_fatal_sigallows those places to be implemented with normal signal exits.
I assume this is talking about seccomp()? :) Should a patch be included in this series to change those?
Quoted 2 lines
Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 10 lines · Kees Cook
Quoted 6 lines · Eric W. Biederman
This is interesting both because it makes
force_sigsegvsimpler and because there are a couple of buggy places in the kernel that calldo_exit(SIGILL)ordo_exit(SIGSYS)because there is no straight forward way today for those places to simply force the exit of a process with the chosen signal. Creatingforce_fatal_sigallows those places to be implemented with normal signal exits.I assume this is talking about
seccomp()? :) Should a patch be included in this series to change those?
Actually it is not talking about seccomp. As far as I can tell seccomp is deliberately only killing a single thread when it calls do_exit.
I am thinking about places where we really want the entire process to die and not just a single thread. Please see the following changes where I actually use force_fatal_sig.
Eric
Re: [PATCH 13/20] signal: Implement force_fatal_sig
Quoted 10 lines · Eric W. Biederman
Quoted 8 lines · Kees Cook
Quoted 6 lines · Eric W. Biederman
This is interesting both because it makes
force_sigsegvsimpler and because there are a couple of buggy places in the kernel that calldo_exit(SIGILL)ordo_exit(SIGSYS)because there is no straight forward way today for those places to simply force the exit of a process with the chosen signal. Creatingforce_fatal_sigallows those places to be implemented with normal signal exits.
Quoted 5 lines
Quoted 2 lines
I assume this is talking about
seccomp()? :) Should a patch be included in this series to change those?Actually it is not talking about seccomp. As far as I can tell seccomp is deliberately only killing a single thread when it calls
do_exit.
Okay, I wasn't entirely sure, but yes, seccomp wants to keep the "kill only 1 thread" option, which is weird, but useful for the threaded seccomp monitor case.
Quoted 3 lines
I am thinking about places where we really want the entire process to die and not just a single thread. Please see the following changes where I actually use
force_fatal_sig.
Yeah, I saw that now. Thanks!
Signature · 1 line
Kees Cook
PATCH 14/20 exit/syscall_user_dispatch: Send ordinary signals on failure
In reply to Eric W. BiedermanUse force_fatal_sig instead of calling do_exit directly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.
When asked Gabriel Krisman Bertazi <[email protected]> said [1]:
Quoted 14 lines
[email protected] (Eric W. Biederman) asked:
Quoted 6 lines
Why does
do_syscal_user_dispatchcalldo_exit(SIGSEGV)anddo_exit(SIGSYS)instead offorce_sig(SIGSEGV)andforce_sig(SIGSYS)?Looking at the code these cases are not expected to happen, so I would be surprised if userspace depends on any particular behaviour on the failure path so I think we can change this.
Hi Eric,
There is not really a good reason, and the use case that originated the feature doesn't rely on it.
Quoted 7 lines
Unless I'm missing yet another problem and others correct me, I think it makes sense to change it as you described.
Quoted 1 line
Is using
do_exitin this way something you copied from seccomp?I'm not sure, its been a while, but I think it might be just that. The first prototype of SUD was implemented as a seccomp mode.
If at some point it becomes interesting we could relax "force_fatal_sig(SIGSEGV)" to instead say "force_sig_fault(SIGSEGV, SEGV_MAPERR, sd->selector)".
I avoid doing that in this patch to avoid making it possible to catch currently uncatchable signals.
Cc: Gabriel Krisman Bertazi <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Andy Lutomirski <[email protected]>
[1] https://lkml.kernel.org/r/[email protected]
--- kernel/entry/syscall_user_dispatch.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
kernel/entry/syscall_user_dispatch.c +8 -4
@@ -47,14 +47,18 @@ bool syscall_user_dispatch(struct pt_regs *regs)* access_ok() is performed once, at prctl time, when * the selector is loaded by userspace. */ -if (unlikely (__get_user (state, sd->selector))) -do_exit (SIGSEGV ); +if (unlikely (__get_user (state, sd->selector))) { +force_fatal_sig (SIGSEGV ); +return true; + }if (likely (state ==SYSCALL_DISPATCH_FILTER_ALLOW ))return false; -if (state !=SYSCALL_DISPATCH_FILTER_BLOCK ) -do_exit (SIGSYS ); +if (state !=SYSCALL_DISPATCH_FILTER_BLOCK ) { +force_fatal_sig (SIGSYS ); +return true; + } } sd->on_dispatch = true;
Signature · 1 line
2.20.1
Re: [PATCH 14/20] exit/syscall_user_dispatch: Send ordinary signals on failure
Quoted 35 lines · Eric W. Biederman
Use
force_fatal_siginstead of callingdo_exitdirectly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.When asked Gabriel Krisman Bertazi <[email protected]> said [1]:
Quoted 14 lines
[email protected] (Eric W. Biederman) asked:
Quoted 6 lines
Why does
do_syscal_user_dispatchcalldo_exit(SIGSEGV)anddo_exit(SIGSYS)instead offorce_sig(SIGSEGV)andforce_sig(SIGSYS)?Looking at the code these cases are not expected to happen, so I would be surprised if userspace depends on any particular behaviour on the failure path so I think we can change this.
Hi Eric,
There is not really a good reason, and the use case that originated the feature doesn't rely on it.
Quoted 7 lines
Unless I'm missing yet another problem and others correct me, I think it makes sense to change it as you described.
Quoted 1 line
Is using
do_exitin this way something you copied from seccomp?I'm not sure, its been a while, but I think it might be just that. The first prototype of SUD was implemented as a seccomp mode.
If at some point it becomes interesting we could relax "
force_fatal_sig(SIGSEGV)" to instead say "force_sig_fault(SIGSEGV, SEGV_MAPERR, sd->selector)".I avoid doing that in this patch to avoid making it possible to catch currently uncatchable signals.
Quoted 6 lines
Cc: Gabriel Krisman Bertazi <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Andy Lutomirski <[email protected]>
[1] https://lkml.kernel.org/r/[email protected]Signed-off-by "Eric W. Biederman" <[email protected]>
Yeah, looks good. Should be no visible behavior change.
Signature · 1 line
Kees Cook
Re: [PATCH 14/20] exit/syscall_user_dispatch: Send ordinary signals on failure
Quoted 7 lines · Kees Cook
Quoted 4 lines · Eric W. Biederman
Use
force_fatal_siginstead of callingdo_exitdirectly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.Yeah, looks good. Should be no visible behavior change.
It is observable in that an entire multi-threaded process gets terminated instead of a single thread. But since these events should be handling of extra-ordinary events I don't expect there is anyone who wants to have a thread of their process survive.
Eric
Re: [PATCH 14/20] exit/syscall_user_dispatch: Send ordinary signals on failure
Quoted 8 lines · Eric W. Biederman
Quoted 6 lines · Kees Cook
Quoted 4 lines · Eric W. Biederman
Use
force_fatal_siginstead of callingdo_exitdirectly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.
Quoted 6 lines
Quoted 1 line
Yeah, looks good. Should be no visible behavior change.
It is observable in that an entire multi-threaded process gets terminated instead of a single thread. But since these events should be handling of extra-ordinary events I don't expect there is anyone who wants to have a thread of their process survive.
Right -- sorry, I should have said that more clearly: "Besides the single thread death now taking the whole process, there's not behavior change (i.e. the signal delivery)." Still looks good to me.
Signature · 1 line
Kees Cook
Re: [PATCH 14/20] exit/syscall_user_dispatch: Send ordinary signals on failure
Quoted 11 lines · Kees Cook
Quoted 10 lines · Eric W. Biederman
Quoted 7 lines · Kees Cook
Quoted 4 lines · Eric W. Biederman
Use
force_fatal_siginstead of callingdo_exitdirectly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.Yeah, looks good. Should be no visible behavior change.
Quoted 8 lines
Quoted 4 lines
It is observable in that an entire multi-threaded process gets terminated instead of a single thread. But since these events should be handling of extra-ordinary events I don't expect there is anyone who wants to have a thread of their process survive.
Right -- sorry, I should have said that more clearly: "Besides the single thread death now taking the whole process, there's not behavior change (i.e. the signal delivery)." Still looks good to me.
Yes. I just didn't want that single vs multi-thread case to sneak up on people. Especially since that is part of the questionable behavior that I am sorting out.
Eric
Andy Lutomirski
maintainerRe: [PATCH 14/20] exit/syscall_user_dispatch: Send ordinary signals on failure
In reply to Kees CookQuoted 43 lines · Kees Cook
Quoted 35 lines · Eric W. Biederman
Use
force_fatal_siginstead of callingdo_exitdirectly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.When asked Gabriel Krisman Bertazi <[email protected]> said [1]:
Quoted 14 lines
[email protected] (Eric W. Biederman) asked:
Quoted 6 lines
Why does
do_syscal_user_dispatchcalldo_exit(SIGSEGV)anddo_exit(SIGSYS)instead offorce_sig(SIGSEGV)andforce_sig(SIGSYS)?Looking at the code these cases are not expected to happen, so I would be surprised if userspace depends on any particular behaviour on the failure path so I think we can change this.
Hi Eric,
There is not really a good reason, and the use case that originated the feature doesn't rely on it.
Quoted 7 lines
Unless I'm missing yet another problem and others correct me, I think it makes sense to change it as you described.
Quoted 1 line
Is using
do_exitin this way something you copied from seccomp?I'm not sure, its been a while, but I think it might be just that. The first prototype of SUD was implemented as a seccomp mode.
If at some point it becomes interesting we could relax "
force_fatal_sig(SIGSEGV)" to instead say "force_sig_fault(SIGSEGV, SEGV_MAPERR, sd->selector)".I avoid doing that in this patch to avoid making it possible to catch currently uncatchable signals.
Quoted 6 lines
Cc: Gabriel Krisman Bertazi <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Andy Lutomirski <[email protected]>
[1] https://lkml.kernel.org/r/[email protected]Signed-off-by "Eric W. Biederman" <[email protected]>
Quoted 4 lines
Yeah, looks good. Should be no visible behavior change.
Reviewed-by Kees Cook <[email protected]>
I'm confused. Before this series, this error path would unconditionally kill the task (other than the race condition in force_sigsegv(), but at least a well-behaved task would get killed). Now a signal handler might be invoked, and it would be invoked after the syscall that triggered the fault got processed as a no-op. If the signal handler never returns, that's fine, but if the signal handler *does* return, the process might be in an odd state. For SIGSYS, this behavior is probably fine, but having SIGSEGV swallow a syscall seems like a mistake.
Maybe rewind (approximately!) the syscall? Or actually send SIGSYS? Or actually make the signal uncatchable?
--Andy
Re: [PATCH 14/20] exit/syscall_user_dispatch: Send ordinary signals on failure
In reply to Eric W. BiedermanQuoted 40 lines · Eric W. Biederman
Use
force_fatal_siginstead of callingdo_exitdirectly. This ensures the ordinary signal handling path gets invoked, core dumps as appropriate get created, and for multi-threaded processes all of the threads are terminated not just a single thread.When asked Gabriel Krisman Bertazi <[email protected]> said [1]:
Quoted 14 lines
[email protected] (Eric W. Biederman) asked:
Quoted 6 lines
Why does
do_syscal_user_dispatchcalldo_exit(SIGSEGV)anddo_exit(SIGSYS)instead offorce_sig(SIGSEGV)andforce_sig(SIGSYS)?Looking at the code these cases are not expected to happen, so I would be surprised if userspace depends on any particular behaviour on the failure path so I think we can change this.
Hi Eric,
There is not really a good reason, and the use case that originated the feature doesn't rely on it.
Quoted 7 lines
Unless I'm missing yet another problem and others correct me, I think it makes sense to change it as you described.
Quoted 1 line
Is using
do_exitin this way something you copied from seccomp?I'm not sure, its been a while, but I think it might be just that. The first prototype of SUD was implemented as a seccomp mode.
If at some point it becomes interesting we could relax "
force_fatal_sig(SIGSEGV)" to instead say "force_sig_fault(SIGSEGV, SEGV_MAPERR, sd->selector)".I avoid doing that in this patch to avoid making it possible to catch currently uncatchable signals.
Cc: Gabriel Krisman Bertazi <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Andy Lutomirski <[email protected]>
[1] https://lkml.kernel.org/r/[email protected]
Quoted 5 lines
Signed-off-by "Eric W. Biederman" <[email protected]>--- kernel/entry/syscall_user_dispatch.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
Hi Eric,
Feel free to add:
Thanks,
Signature · 1 line
Gabriel Krisman Bertazi
PATCH 15/20 signal/sparc32: Exit with a fatal signal when try_to_clear_window_buffer fails
In reply to Eric W. BiedermanThe function try_to_clear_window_buffer is only called from rtrap_32.c. After it is called the signal pending state is retested, and signals are handled if TIF_SIGPENDING is set. This allows try_to_clear_window_buffer to call force_fatal_signal and then rely on the signal being delivered to kill the process, without any danger of returning to userspace, or otherwise using possible corrupt state on failure.
The functional difference between force_fatal_sig and do_exit is that do_exit will only terminate a single thread, and will never trigger a core-dump. A multi-threaded program for which a single thread terminates unexpectedly is hard to reason about. Calling force_fatal_sig does not give userspace a chance to catch the signal, but otherwise is an ordinary fatal signal exit, and it will trigger a coredump of the offending process if core dumps are enabled.
--- arch/sparc/kernel/windows.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
arch/sparc/kernel/windows.c +4 -2
@@ -121,8 +121,10 @@ 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 ))) -do_exit (SIGILL ); +sizeof (struct reg_window32 ))) { +force_fatal_sig (SIGILL ); +return ; + } } tp->w_saved =0 ; }
Signature · 1 line
2.20.1
Re: [PATCH 15/20] signal/sparc32: Exit with a fatal signal when try_to_clear_window_buffer fails
Quoted 2 lines · Eric W. Biederman
The function
try_to_clear_window_bufferis only called fromrtrap_32.c. After it is called the signal pending state is retested,
nit: rtrap_32.S
Quoted 5 lines
and signals are handled if
TIF_SIGPENDINGis set. This allowstry_to_clear_window_bufferto callforce_fatal_signaland then rely on the signal being delivered to kill the process, without any danger of returning to userspace, or otherwise using possible corrupt state on failure.
The TIF_SIGPENDING test happens in do_notify_resume(), though I see other code before that:
...
call try_to_clear_window_buffer
add %sp, STACKFRAME_SZ, %o0
b signal_p
...signal_p:
andcc %g2, _TIF_DO_NOTIFY_RESUME_MASK, %g0
bz,a ret_trap_continue
ld [%sp + STACKFRAME_SZ + PT_PSR], %t_psr
mov %g2, %o2
mov %l6, %o1
call do_notify_resume
Will the ret_trap_continue always be skipped?
Also I see the "tp->w_saved = 0" never happens due to the "return" in try_to_clear_window_buffer. Is that okay? Only synchronize_user_stack() uses it, and that could be called in do_sigreturn(). Should the "return" be removed?
Signature · 1 line
Kees Cook
Re: [PATCH 15/20] signal/sparc32: Exit with a fatal signal when try_to_clear_window_buffer fails
Quoted 26 lines · Kees Cook
Quoted 2 lines · Eric W. Biederman
The function
try_to_clear_window_bufferis only called fromrtrap_32.c. After it is called the signal pending state is retested,nit:
rtrap_32.SQuoted 5 lines
and signals are handled if
TIF_SIGPENDINGis set. This allowstry_to_clear_window_bufferto callforce_fatal_signaland then rely on the signal being delivered to kill the process, without any danger of returning to userspace, or otherwise using possible corrupt state on failure.The
TIF_SIGPENDINGtest happens indo_notify_resume(), though I see other code before that:...
calltry_to_clear_window_buffer
add %sp,STACKFRAME_SZ, %o0b
signal_p
...signal_p:
andcc %g2,_TIF_DO_NOTIFY_RESUME_MASK, %g0
bz,aret_trap_continue
ld [%sp +STACKFRAME_SZ+PT_PSR], %t_psr
Quoted 5 lines
mov %g2, %o2
mov %l6, %o1
calldo_notify_resumeWill the
ret_trap_continuealways be skipped?
The ret_trap_continue is the break out of the loop. So unless the code is not properly setting the signal to be pending the code should be good.
Quoted 2 lines
Also I see the "
tp->w_saved= 0" never happens due to the "return" intry_to_clear_window_buffer. Is that okay?
It should be. As you point out the w_saved value is only used in generating signal frames. The code in get_signal should never return and should call do_group_exit which calls do_exit, so building signal frames that happens after get_signal returns should never be reached.
Further this is the same way the code makes it to do_exit today.
Also looking at it I think the logic is that w_saved == 0 says that the register windows have been saved on the user mode stack, and that clearly has not happened so I think it would in general be a bug to clear w_saved on failure.
Quoted 3 lines
Only
synchronize_user_stack()
uses it, and that could be called indo_sigreturn(). Should the "return" be removed?
Of course I could be wrong, if David or someone else who knows sparc32 better than me wants to set me straight I would really appreciate it.
Eric
PATCH 16/20 signal/sparc32: In setup_rt_frame and setup_fram use force_fatal_sig
In reply to Eric W. BiedermanModify the 32bit version of setup_rt_frame and setup_frame to act similar to the 64bit version of setup_rt_frame and fail with a signal instead of calling do_exit.
Replacing do_exit(SIGILL) with force_fatal_signal(SIGILL) ensures that the process will be terminated cleanly when the stack frame is invalid, instead of just killing off a single thread and leaving the process is a weird state.
--- arch/sparc/kernel/signal_32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/sparc/kernel/signal_32.c +2 -2
@@ -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)) { -do_exit (SIGILL ); +force_fatal_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)) { -do_exit (SIGILL ); +force_fatal_sig (SIGILL );return -EINVAL ; }
Signature · 1 line
2.20.1
Re: [PATCH 16/20] signal/sparc32: In setup_rt_frame and setup_fram use force_fatal_sig
Quoted 4 lines · Eric W. Biederman
Modify the 32bit version of
setup_rt_frameandsetup_frameto act similar to the 64bit version ofsetup_rt_frameand fail with a signal instead of callingdo_exit.
Quoted 8 lines
Replacing
do_exit(SIGILL)withforce_fatal_signal(SIGILL)ensures that the process will be terminated cleanly when the stack frame is invalid, instead of just killing off a single thread and leaving the process is a weird state.Cc David Miller <[email protected]>Signed-off-by Eric W. Biederman <[email protected]>
Nicely already had the return path written. :)
Signature · 1 line
Kees Cook
PATCH 17/20 signal/x86: In emulate_vsyscall force a signal instead of calling do_exit
In reply to Eric W. BiedermanDirectly calling do_exit with a signal number has the problem that all of the side effects of the signal don't happen, such as killing all of the threads of a process instead of just the calling thread.
So replace do_exit(SIGSYS) with force_fatal_sig(SIGSYS) which causes the signal handling to take it's normal path and work as expected.
--- arch/x86/entry/vsyscall/vsyscall_64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/entry/vsyscall/vsyscall_64.c +2 -1
@@ -226,7 +226,8 @@ 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" ); -do_exit (SIGSYS ); +force_fatal_sig (SIGSYS ); +return true; } regs->orig_ax =-1 ;if (tmp)
Signature · 1 line
2.20.1
Re: [PATCH 17/20] signal/x86: In emulate_vsyscall force a signal instead of calling do_exit
Quoted 20 lines · Eric W. Biederman
Directly calling
do_exitwith a signal number has the problem that all of the side effects of the signal don't happen, such as killing all of the threads of a process instead of just the calling thread.So replace
do_exit(SIGSYS)withforce_fatal_sig(SIGSYS)which causes the signal handling to take it's normal path and work as expected.Cc Andy Lutomirski <[email protected]>Signed-off-by "Eric W. Biederman" <[email protected]>--- arch/x86/entry/vsyscall/vsyscall_64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)arch/x86/entry/vsyscall/vsyscall_64.c +0
-0
arch/x86/entry/vsyscall/vsyscall_64.c bool emulate_vsyscall(unsigned long error_code, · line 233
Quoted 9 lines
arch/x86/entry/vsyscall/vsyscall_64.c +2
-1@@ -226,7 +226,8 @@ 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" ); -do_exit (SIGSYS ); +force_fatal_sig (SIGSYS ); +return true; } regs->orig_ax =-1 ;if (tmp)
This looks correct to me, but please double-check the x86 selftests if you haven't already.
Signature · 1 line
Kees Cook
PATCH 18/20 exit/rtl8723bs: Replace the macro thread_exit with a simple return 0
In reply to Eric W. BiedermanEvery place thread_exit is called is at the end of a function started with kthread_run. The code in kthread_run has arranged things so a kernel thread can just return and do_exit will be called.
So just have the threads return instead of calling complete_and_exit.
--- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_xmit.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- drivers/staging/rtl8723bs/include/osdep_service_linux.h | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-)
drivers/staging/rtl8723bs/core/rtw_cmd.c +1 -1
@@ -524,7 +524,7 @@ int rtw_cmd_thread(void *context)complete (&pcmdpriv->terminate_cmdthread_comp);atomic_set (&(pcmdpriv->cmdthd_running), false); -thread_exit (); +return 0 ; }/*
drivers/staging/rtl8723bs/core/rtw_xmit.c +1 -1
@@ -2491,7 +2491,7 @@ int rtw_xmit_thread(void *context)complete (&padapter->xmitpriv.terminate_xmitthread_comp); -thread_exit (); +return 0 ; }void rtw_sctx_init (struct submit_ctx *sctx,int timeout_ms)
drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +1 -1
@@ -435,7 +435,7 @@ int rtl8723bs_xmit_thread(void *context)complete (&pxmitpriv->SdioXmitTerminate); -thread_exit (); +return 0 ; } s32 rtl8723bs_mgnt_xmit(
drivers/staging/rtl8723bs/include/osdep_service_linux.h +0 -2
@@ -45,8 +45,6 @@spinlock_t lock; }; -#define thread_exit () complete_and_exit(NULL, 0) -static inline struct list_head *get_next (struct list_head *list) {return list->next;
Signature · 1 line
2.20.1
Re: [PATCH 18/20] exit/rtl8723bs: Replace the macro thread_exit with a simple return 0
Quoted 6 lines · Eric W. Biederman
Every place
thread_exitis called is at the end of a function started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have the threads return instead of calling
complete_and_exit.
Quoted 7 lines
Signed-off-by "Eric W. Biederman" <[email protected]>--- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_xmit.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- drivers/staging/rtl8723bs/include/osdep_service_linux.h | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-)
You "forgot" to cc: the linux-staging and the staging driver maintainer on these drivers/staging/ changes...
Anyway, they look fine to me, but you will get some conflicts with some of these changes based on cleanups already in my staging-next tree (in linux-next if you want to see them). But feel free to take these all in your tree if that makes it easier:
Re: [PATCH 18/20] exit/rtl8723bs: Replace the macro thread_exit with a simple return 0
Quoted 15 lines · Greg KH
Quoted 6 lines · Eric W. Biederman
Every place
thread_exitis called is at the end of a function started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have the threads return instead of calling
complete_and_exit.Quoted 7 lines
Signed-off-by "Eric W. Biederman" <[email protected]>--- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/core/rtw_xmit.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- drivers/staging/rtl8723bs/include/osdep_service_linux.h | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-)
Quoted 2 lines
You "forgot" to cc: the linux-staging and the staging driver maintainer on these
drivers/staging/changes...
Yes I did. Sorry about that.
Quoted 4 lines
Anyway, they look fine to me, but you will get some conflicts with some of these changes based on cleanups already in my staging-next tree (in linux-next if you want to see them). But feel free to take these all in your tree if that makes it easier:
I just did a test merge and there was one file that was completely removed and one file with had changes a line or two above where my code changed. So nothing too difficult to result.
I don't really mind either way. But keeping them all in one tree makes them easier to keep track of, and allows me to do things like see if I can remove EXPORT_SYMBOL(do_exit) as Christoph suggested.
Eric
Quoted 1 line
Re: [PATCH 18/20] exit/rtl8723bs: Replace the macro thread_exit with a simple return 0
In reply to Eric W. BiedermanQuoted 7 lines · Eric W. Biederman
Every place
thread_exitis called is at the end of a function started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have the threads return instead of calling
complete_and_exit.Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
PATCH 19/20 exit/rtl8712: Replace the macro thread_exit with a simple return 0
In reply to Eric W. BiedermanThe macro thread_exit is called is at the end of a function started with kthread_run. The code in kthread_run has arranged things so a kernel thread can just return and do_exit will be called.
So just have the cmd_thread return instead of calling complete_and_exit.
--- drivers/staging/rtl8712/osdep_service.h | 1 - drivers/staging/rtl8712/rtl8712_cmd.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-)
drivers/staging/rtl8712/osdep_service.h +0 -1
@@ -37,7 +37,6 @@ struct __queue {#define _pkt struct sk_buff#define _buffer unsigned char -#define thread_exit () complete_and_exit(NULL, 0)#define _init_queue (pqueue) \ do { \
drivers/staging/rtl8712/rtl8712_cmd.c +1 -1
@@ -393,7 +393,7 @@ int r8712_cmd_thread(void *context)r8712_free_cmd_obj (pcmd); }while (1 );complete (&pcmdpriv->terminate_cmdthread_comp); -thread_exit (); +return 0 ; }void r8712_event_handle (struct _adapter *padapter,__le32 *peventbuf)
Signature · 1 line
2.20.1
Re: [PATCH 19/20] exit/rtl8712: Replace the macro thread_exit with a simple return 0
Quoted 7 lines · Eric W. Biederman
The macro
thread_exitis called is at the end of a function started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have the
cmd_threadreturn instead of callingcomplete_and_exit.Signed-off-by "Eric W. Biederman" <[email protected]>
Re: [PATCH 19/20] exit/rtl8712: Replace the macro thread_exit with a simple return 0
In reply to Eric W. BiedermanQuoted 7 lines · Eric W. Biederman
The macro
thread_exitis called is at the end of a function started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have the
cmd_threadreturn instead of callingcomplete_and_exit.Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
PATCH 20/20 exit/r8188eu: Replace the macro thread_exit with a simple return 0
In reply to Eric W. BiedermanThe macro thread_exit is called is at the end of functions started with kthread_run. The code in kthread_run has arranged things so a kernel thread can just return and do_exit will be called.
So just have rtw_cmd_thread and mp_xmit_packet_thread return instead of calling complete_and_exit.
--- drivers/staging/r8188eu/core/rtw_cmd.c | 2 +- drivers/staging/r8188eu/core/rtw_mp.c | 2 +- drivers/staging/r8188eu/include/osdep_service.h | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-)
drivers/staging/r8188eu/core/rtw_cmd.c +1 -1
@@ -347,7 +347,7 @@ int rtw_cmd_thread(void *context)up (&pcmdpriv->terminate_cmdthread_sema); -thread_exit (); +return 0 ; }u8 rtw_setstandby_cmd (struct adapter *padapter,uint action)
drivers/staging/r8188eu/core/rtw_mp.c +1 -1
@@ -580,7 +580,7 @@ static int mp_xmit_packet_thread(void *context) pmptx->pallocated_buf =NULL ; pmptx->stop =1 ; -thread_exit (); +return 0 ; }void fill_txdesc_for_mp (struct adapter *padapter,struct tx_desc *ptxdesc)
drivers/staging/r8188eu/include/osdep_service.h +0 -2
@@ -49,8 +49,6 @@ struct __queue {spinlock_t lock; }; -#define thread_exit () complete_and_exit(NULL, 0) -static inline struct list_head *get_list_head (struct __queue *queue) {return (&(queue->queue));
Signature · 1 line
2.20.1
Re: [PATCH 20/20] exit/r8188eu: Replace the macro thread_exit with a simple return 0
Quoted 8 lines · Eric W. Biederman
The macro
thread_exitis called is at the end of functions started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have
rtw_cmd_threadandmp_xmit_packet_threadreturn instead of callingcomplete_and_exit.Signed-off-by "Eric W. Biederman" <[email protected]>
Re: [PATCH 20/20] exit/r8188eu: Replace the macro thread_exit with a simple return 0
In reply to Eric W. BiedermanQuoted 8 lines · Eric W. Biederman
The macro
thread_exitis called is at the end of functions started withkthread_run. The code inkthread_runhas arranged things so a kernel thread can just return anddo_exitwill be called.So just have
rtw_cmd_threadandmp_xmit_packet_threadreturn instead of callingcomplete_and_exit.Signed-off-by "Eric W. Biederman" <[email protected]>
Signature · 1 line
Kees Cook
[PATCH 21/20] signal: Replace force_sigsegv(SIGSEGV) with force_fatal_sig(SIGSEGV)
In reply to Eric W. BiedermanNow that force_fatal_sig exists it is unnecessary and a bit confusing to use force_sigsegv in cases where the simpler force_fatal_sig is wanted. So change every instance we can to make the code clearer.
--- arch/arc/kernel/process.c | 2 +- arch/m68k/kernel/traps.c | 2 +- arch/powerpc/kernel/signal_32.c | 2 +- arch/powerpc/kernel/signal_64.c | 4 ++-- arch/s390/kernel/traps.c | 2 +- arch/um/kernel/trap.c | 2 +- arch/x86/kernel/vm86_32.c | 2 +- fs/exec.c | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-)
arch/arc/kernel/process.c +1 -1
@@ -294,7 +294,7 @@ int elf_check_arch(const struct elf32_hdr *x) eflags = x->e_flags;if ((eflags &EF_ARC_OSABI_MSK ) !=EF_ARC_OSABI_CURRENT ) {pr_err ("ABI mismatch - you need newer toolchain\n" ); -force_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV );return 0 ; }
arch/m68k/kernel/traps.c +1 -1
@@ -1150,7 +1150,7 @@ asmlinkage void set_esp0(unsigned long ssp)*/ asmlinkage void fpsp040_die(void) { -force_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV ); }#ifdef CONFIG_M68KFPU_EMU
arch/powerpc/kernel/signal_32.c +1 -1
@@ -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_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV );return -EFAULT ; }
arch/powerpc/kernel/signal_64.c +2 -2
@@ -704,7 +704,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,*/ if (__get_user_sigset (&set, &new_ctx->uc_sigmask)) { -force_sigsegv (SIGSEGV ); +force_fatal_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_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV );return -EFAULT ; }user_read_access_end ();
arch/s390/kernel/traps.c +1 -1
@@ -84,7 +84,7 @@ static void default_trap_handler(struct pt_regs *regs) {if (user_mode (regs)) {report_user_fault (regs,SIGSEGV ,0 ); -force_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV ); }else die (regs,"Unknown program exception" ); }
arch/um/kernel/trap.c +1 -1
@@ -158,7 +158,7 @@ static void bad_segv(struct faultinfo fi, unsigned long ip)void fatal_sigsegv (void ) { -force_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV );do_signal (¤t->thread.regs);/* * This is to tell gcc that we're not returning - do_signal
arch/x86/kernel/vm86_32.c +1 -1
@@ -159,7 +159,7 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)user_access_end (); Efault:pr_alert ("could not access userspace vm86 info\n" ); -force_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV ); }static int do_vm86_irq_handling (int subfunction,int irqnumber);
fs/exec.c +1 -1
@@ -1852,7 +1852,7 @@ static int bprm_execve(struct linux_binprm *bprm,* SIGSEGV. */ if (bprm->point_of_no_return && !fatal_signal_pending (current)) -force_sigsegv (SIGSEGV ); +force_fatal_sig (SIGSEGV ); out_unmark: current->fs->in_exec =0 ;
Signature · 1 line
2.20.1
Re: [PATCH 21/20] signal: Replace force_sigsegv(SIGSEGV) with force_fatal_sig(SIGSEGV)
Hi Eric,
Patch 21/20?
Quoted 7 lines · Eric W. Biederman
Now that
force_fatal_sigexists it is unnecessary and a bit confusing to useforce_sigsegvin cases where the simplerforce_fatal_sigis wanted. So change every instance we can to make the code clearer.Signed-off-by "Eric W. Biederman" <[email protected]>arch/m68k/kernel/traps.c | 2 +-
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Re: [PATCH 21/20] signal: Replace force_sigsegv(SIGSEGV) with force_fatal_sig(SIGSEGV)
Quoted 3 lines · Geert Uytterhoeven
Hi Eric,
Patch 21/20?
In reviewing another part of the patchset Linus asked if force_sigsegv could go away. It can't completely but I can get this far.
Given that it is just a cleanup it makes most sense to me as an additional patch on top of what is already here.
Quoted 11 lines
Quoted 7 lines · Eric W. Biederman
Now that
force_fatal_sigexists it is unnecessary and a bit confusing to useforce_sigsegvin cases where the simplerforce_fatal_sigis wanted. So change every instance we can to make the code clearer.Signed-off-by "Eric W. Biederman" <[email protected]>arch/m68k/kernel/traps.c | 2 +-
Thank you.
Eric
Philippe Mathieu-Daudé
Reviewed-byRe: [PATCH 21/20] signal: Replace force_sigsegv(SIGSEGV) with force_fatal_sig(SIGSEGV)
In reply to Eric W. BiedermanQuoted 7 lines · Eric W. Biederman
Now that
force_fatal_sigexists it is unnecessary and a bit confusing to useforce_sigsegvin cases where the simplerforce_fatal_sigis wanted. So change every instance we can to make the code clearer.Signed-off-by "Eric W. Biederman" <[email protected]>
Quoted 10 lines
--- arch/arc/kernel/process.c | 2 +- arch/m68k/kernel/traps.c | 2 +- arch/powerpc/kernel/signal_32.c | 2 +- arch/powerpc/kernel/signal_64.c | 4 ++-- arch/s390/kernel/traps.c | 2 +- arch/um/kernel/trap.c | 2 +- arch/x86/kernel/vm86_32.c | 2 +- fs/exec.c | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-)