The 68030 Users Manual says that address errors occur immediately they are detected during instruction prefetch. The instruction pipeline allows prefetch to overlap with other instructions which means an address error can be taken during execution of a different instruction. Both a bus error and an address error may produce a format 0xB exception frame. Regarding those frames, the UM says the PC contained therein has the "address of instruction in execution when fault occurred -- may not be the instruction which generated the faulted bus cycle". In the bus error case, the USP in that frame is not reliable (like the PC). We should not rely on it in the address error case. The address error case always produces a SIGBUS. That signal may be caught and the exception fixed up. If a debugger or emulator were to do so, this patch would theoretically prevent user stack corruption. Cc: Michael Schmitz <schmitzmic@xxxxxxxxx> Cc: Andreas Schwab <schwab@xxxxxxxxxxxxxx> Link: https://lore.kernel.org/linux-m68k/20230429080410.8993-1-schmitzmic@xxxxxxxxx/ Co-developed-by: Michael Schmitz <schmitzmic@xxxxxxxxx> Signed-off-by: Finn Thain <fthain@xxxxxxxxxxxxxx> --- Michael, as co-developer, this will need your signed-off-by. --- arch/m68k/kernel/signal.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index b9f6908a31bc..8aeafbb083f7 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -858,11 +858,16 @@ static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs * } static inline void __user * -get_sigframe(struct ksignal *ksig, size_t frame_size) +get_sigframe(struct ksignal *ksig, struct pt_regs *tregs, size_t frame_size) { unsigned long usp = sigsp(rdusp(), ksig); + unsigned long gap = 0; - return (void __user *)((usp - frame_size) & -8UL); + if (CPU_IS_020_OR_030 && tregs->format == 0xb) + /* USP is unreliable so use worst-case value */ + gap = 256; + + return (void __user *)((usp - gap - frame_size) & -8UL); } static int setup_frame(struct ksignal *ksig, sigset_t *set, @@ -880,7 +885,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, return -EFAULT; } - frame = get_sigframe(ksig, sizeof(*frame) + fsize); + frame = get_sigframe(ksig, tregs, sizeof(*frame) + fsize); if (fsize) err |= copy_to_user (frame + 1, regs + 1, fsize); @@ -952,7 +957,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, return -EFAULT; } - frame = get_sigframe(ksig, sizeof(*frame)); + frame = get_sigframe(ksig, tregs, sizeof(*frame)); if (fsize) err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize); -- 2.37.5