From: Andreas Schwab <schwab@xxxxxxxxxxxxxx> Signal delivery should only happen at insn boundaries, but due to the way the 030 handles return from bus error exceptions (the insn is resumed, not restarted like on the 040/060) the kernel may do it in the middle of the faulting insn. For example, a page fault can happen during execution of moveml %a2-%a3/%a5,%sp@- This then produces a format 0xB exception frame containing an unreliable USP value. That value gets used to calculate the location for a signal frame and the end result is a corrupted the user stack. This stack corruption was observed in dash (actually in glibc) where it showed up as an intermittent "stack smashing detected" failure following SIGCHLD signal delivery. The failure was hard to reproduce because delivery of the signal races with the page fault and because the kernel places an unpredictable gap of up to 7 bytes between the USP and the signal frame, which is often sufficient to prevent stack corruption. Reported-and-tested-by: Stan Johnson <userm57@xxxxxxxxx> Link: https://lore.kernel.org/all/CAMuHMdW3yD22_ApemzW_6me3adq6A458u1_F0v-1EYwK_62jPA@xxxxxxxxxxxxxx/ Cc: Michael Schmitz <schmitzmic@xxxxxxxxx> Cc: Andreas Schwab <schwab@xxxxxxxxxxxxxx> Signed-off-by: Finn Thain <fthain@xxxxxxxxxxxxxx> --- This is the same patch that Andreas sent, excepting a minor change to the commentary and a 'fallthrough' statement. Andreas, as the author, this will need your signed-off-by. --- arch/m68k/kernel/entry.S | 6 +++++- arch/m68k/kernel/traps.c | 10 ++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 4dd2fd7acba9..77b558dad14b 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -117,7 +117,11 @@ ENTRY(buserr) movel %sp,%sp@- | stack frame pointer argument jbsr buserr_c addql #4,%sp - jra ret_from_exception + | deliver no signals if the fault occurred with an insn in progress + | (on the 020/030) + tstl %d0 + jeq ret_from_exception + RESTORE_ALL ENTRY(trap) SAVE_ALL_INT diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index a700807c9b6d..f535054d1e2a 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -751,8 +751,10 @@ static inline void access_errorcf(unsigned int fs, struct frame *fp) } #endif /* CONFIG_COLDFIRE CONFIG_MMU */ -asmlinkage void buserr_c(struct frame *fp) +asmlinkage int buserr_c(struct frame *fp) { + int not_insn_boundary = 0; + /* Only set esp0 if coming from user mode */ if (user_mode(&fp->ptregs)) current->thread.esp0 = (unsigned long) fp; @@ -793,8 +795,10 @@ asmlinkage void buserr_c(struct frame *fp) break; #endif #if defined (CPU_M68020_OR_M68030) - case 0xa: case 0xb: + not_insn_boundary = 1; + fallthrough; + case 0xa: bus_error030 (fp); break; #endif @@ -803,6 +807,8 @@ asmlinkage void buserr_c(struct frame *fp) pr_debug("Unknown SIGSEGV - 4\n"); force_sig(SIGSEGV); } + + return not_insn_boundary; } -- 2.37.5