Re: [PATCH 1/3] m68k: handle arrivals of multiple signals correctly

Michael Schmitz
2021-09-15T22:09:10Z

Hi Al,

On 26/07/21 5:19 am, Al Viro wrote:
> When we have several pending signals, have entered with the kernel
> with large exception frame *and* have already built at least one
> sigframe, regs->stkadj is going to be non-zero and regs->format/sr/pc
> are going to be junk - the real values are in shifted exception stack
> frame we'd built when putting together the first sigframe.
>
> If that happens, subsequent sigframes are going to be garbage.
> Not hard to fix - just need to find the "adjusted" frame first
> and look for format/vector/sr/pc in it.
>
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Looks good to me. What's more, it fixes a number of long standing issues
dating back to the 4.10 ages - see discussions at:

https://lore.kernel.org/r/7517d306-21ad-daa1-a2fb-b273211cb588@gmail.com

https://lore.kernel.org/r/ed2ca322-b957-cd52-8d2f-a8edd2785625@linux-m68k.org

- so should be applied to -stable IMO.

Tested by me on 68030 - Finn Thain did some testing on 68040 and might
add his own tag.

Tested-by: Michael Schmitz <schmitzmic@gmail.com>

Reviewed-by: Michael Schmitz <schmitzmic@gmail.com>

> ---
> arch/m68k/kernel/signal.c | 88 ++++++++++++++++++++++-------------------------
> 1 file changed, 42 insertions(+), 46 deletions(-)
>
> diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c
> index 8f215e79e70e..cd11eb101eac 100644
> --- a/arch/m68k/kernel/signal.c
> +++ b/arch/m68k/kernel/signal.c
> @@ -447,7 +447,7 @@ static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)
>
> if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) {
> fpu_version = sc->sc_fpstate[0];
> - if (CPU_IS_020_OR_030 &&
> + if (CPU_IS_020_OR_030 && !regs->stkadj &&
> regs->vector >= (VEC_FPBRUC * 4) &&
> regs->vector <= (VEC_FPNAN * 4)) {
> /* Clear pending exception in 68882 idle frame */
> @@ -510,7 +510,7 @@ static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *
> if (!(CPU_IS_060 || CPU_IS_COLDFIRE))
> context_size = fpstate[1];
> fpu_version = fpstate[0];
> - if (CPU_IS_020_OR_030 &&
> + if (CPU_IS_020_OR_030 && !regs->stkadj &&
> regs->vector >= (VEC_FPBRUC * 4) &&
> regs->vector <= (VEC_FPNAN * 4)) {
> /* Clear pending exception in 68882 idle frame */
> @@ -832,18 +832,24 @@ asmlinkage int do_rt_sigreturn(struct pt_regs *regs, struct switch_stack *sw)
> return 0;
> }
>
> +static inline struct pt_regs *rte_regs(struct pt_regs *regs)
> +{
> + return (void *)regs + regs->stkadj;
> +}
> +
> static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
> unsigned long mask)
> {
> + struct pt_regs *tregs = rte_regs(regs);
> sc->sc_mask = mask;
> sc->sc_usp = rdusp();
> sc->sc_d0 = regs->d0;
> sc->sc_d1 = regs->d1;
> sc->sc_a0 = regs->a0;
> sc->sc_a1 = regs->a1;
> - sc->sc_sr = regs->sr;
> - sc->sc_pc = regs->pc;
> - sc->sc_formatvec = regs->format << 12 | regs->vector;
> + sc->sc_sr = tregs->sr;
> + sc->sc_pc = tregs->pc;
> + sc->sc_formatvec = tregs->format << 12 | tregs->vector;
> save_a5_state(sc, regs);
> save_fpu_state(sc, regs);
> }
> @@ -851,6 +857,7 @@ static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
> static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs)
> {
> struct switch_stack *sw = (struct switch_stack *)regs - 1;
> + struct pt_regs *tregs = rte_regs(regs);
> greg_t __user *gregs = uc->uc_mcontext.gregs;
> int err = 0;
>
> @@ -871,9 +878,9 @@ static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *
> err |= __put_user(sw->a5, &gregs[13]);
> err |= __put_user(sw->a6, &gregs[14]);
> err |= __put_user(rdusp(), &gregs[15]);
> - err |= __put_user(regs->pc, &gregs[16]);
> - err |= __put_user(regs->sr, &gregs[17]);
> - err |= __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec);
> + err |= __put_user(tregs->pc, &gregs[16]);
> + err |= __put_user(tregs->sr, &gregs[17]);
> + err |= __put_user((tregs->format << 12) | tregs->vector, &uc->uc_formatvec);
> err |= rt_save_fpu_state(uc, regs);
> return err;
> }
> @@ -890,13 +897,14 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
> struct pt_regs *regs)
> {
> struct sigframe __user *frame;
> - int fsize = frame_extra_sizes(regs->format);
> + struct pt_regs *tregs = rte_regs(regs);
> + int fsize = frame_extra_sizes(tregs->format);
> struct sigcontext context;
> int err = 0, sig = ksig->sig;
>
> if (fsize < 0) {
> pr_debug("setup_frame: Unknown frame format %#x\n",
> - regs->format);
> + tregs->format);
> return -EFAULT;
> }
>
> @@ -907,7 +915,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
>
> err |= __put_user(sig, &frame->sig);
>
> - err |= __put_user(regs->vector, &frame->code);
> + err |= __put_user(tregs->vector, &frame->code);
> err |= __put_user(&frame->sc, &frame->psc);
>
> if (_NSIG_WORDS > 1)
> @@ -934,33 +942,27 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,
> push_cache ((unsigned long) &frame->retcode);
>
> /*
> - * Set up registers for signal handler. All the state we are about
> - * to destroy is successfully copied to sigframe.
> - */
> - wrusp ((unsigned long) frame);
> - regs->pc = (unsigned long) ksig->ka.sa.sa_handler;
> - adjustformat(regs);
> -
> - /*
> * This is subtle; if we build more than one sigframe, all but the
> * first one will see frame format 0 and have fsize == 0, so we won't
> * screw stkadj.
> */
> - if (fsize)
> + if (fsize) {
> regs->stkadj = fsize;
> -
> - /* Prepare to skip over the extra stuff in the exception frame. */
> - if (regs->stkadj) {
> - struct pt_regs *tregs =
> - (struct pt_regs *)((ulong)regs + regs->stkadj);
> + tregs = rte_regs(regs);
> pr_debug("Performing stackadjust=%04lx\n", regs->stkadj);
> - /* This must be copied with decreasing addresses to
> - handle overlaps. */
> tregs->vector = 0;
> tregs->format = 0;
> - tregs->pc = regs->pc;
> tregs->sr = regs->sr;
> }
> +
> + /*
> + * Set up registers for signal handler. All the state we are about
> + * to destroy is successfully copied to sigframe.
> + */
> + wrusp ((unsigned long) frame);
> + tregs->pc = (unsigned long) ksig->ka.sa.sa_handler;
> + adjustformat(regs);
> +
> return 0;
> }
>
> @@ -968,7 +970,8 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
> struct pt_regs *regs)
> {
> struct rt_sigframe __user *frame;
> - int fsize = frame_extra_sizes(regs->format);
> + struct pt_regs *tregs = rte_regs(regs);
> + int fsize = frame_extra_sizes(tregs->format);
> int err = 0, sig = ksig->sig;
>
> if (fsize < 0) {
> @@ -1019,33 +1022,26 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
> push_cache ((unsigned long) &frame->retcode);
>
> /*
> - * Set up registers for signal handler. All the state we are about
> - * to destroy is successfully copied to sigframe.
> - */
> - wrusp ((unsigned long) frame);
> - regs->pc = (unsigned long) ksig->ka.sa.sa_handler;
> - adjustformat(regs);
> -
> - /*
> * This is subtle; if we build more than one sigframe, all but the
> * first one will see frame format 0 and have fsize == 0, so we won't
> * screw stkadj.
> */
> - if (fsize)
> + if (fsize) {
> regs->stkadj = fsize;
> -
> - /* Prepare to skip over the extra stuff in the exception frame. */
> - if (regs->stkadj) {
> - struct pt_regs *tregs =
> - (struct pt_regs *)((ulong)regs + regs->stkadj);
> + tregs = rte_regs(regs);
> pr_debug("Performing stackadjust=%04lx\n", regs->stkadj);
> - /* This must be copied with decreasing addresses to
> - handle overlaps. */
> tregs->vector = 0;
> tregs->format = 0;
> - tregs->pc = regs->pc;
> tregs->sr = regs->sr;
> }
> +
> + /*
> + * Set up registers for signal handler. All the state we are about
> + * to destroy is successfully copied to sigframe.
> + */
> + wrusp ((unsigned long) frame);
> + tregs->pc = (unsigned long) ksig->ka.sa.sa_handler;
> + adjustformat(regs);
> return 0;
> }
>