This commit updates the behavior of signal handling under !MMU environment. 1) the stack preparation for the signal handlers and 2) restoration of stack after rt_sigreturn(2) syscall. Those are needed as the stack usage on vfork(2) syscall is different.
It also adds the follow up routine for SIGSEGV as a signal delivery runs in the same stack frame while we have to avoid endless SIGSEGV. Signed-off-by: Hajime Tazaki <thehaj...@gmail.com> --- arch/um/os-Linux/signal.c | 16 +++++++++++++++- arch/x86/um/signal.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index c0d1fb1fc0c4..de3ed8fc0268 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -55,7 +55,15 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc) struct uml_pt_regs r; int save_errno = errno; - r.is_user = 0; +#ifndef CONFIG_MMU + memset(&r, 0, sizeof(r)); + /* mark is_user=1 when the IP is from userspace code. */ + if (mc && (REGS_IP(mc->gregs) > uml_reserved + && REGS_IP(mc->gregs) < high_physmem)) + r.is_user = 1; + else +#endif + r.is_user = 0; if (sig == SIGSEGV) { /* For segfaults, we want the data from the sigcontext. */ get_regs_from_mc(&r, mc); @@ -69,6 +77,12 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc) (*sig_info[sig])(sig, si, &r); errno = save_errno; + +#ifndef CONFIG_MMU + /* force handle signals after rt_sigreturn() */ + if (r.is_user && sig == SIGSEGV) + mc_set_regs_ip_relay(mc); +#endif } /* diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c index 75087e85b6fd..e1b3a87ddc5d 100644 --- a/arch/x86/um/signal.c +++ b/arch/x86/um/signal.c @@ -370,6 +370,13 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, frame = (struct rt_sigframe __user *) round_down(stack_top - sizeof(struct rt_sigframe), 16); +#ifndef CONFIG_MMU + /* + * the sig_frame on !MMU needs be aligned for SSE as + * the frame is used as-is. + */ + math_size = round_down(math_size, 16); +#endif /* Add required space for math frame */ frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size); @@ -417,6 +424,18 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, /* could use a vstub here */ return err; +#ifndef CONFIG_MMU + /* + * we need to push handler address at top of stack, as + * __kernel_vsyscall, called after this returns with ret with + * stack contents, thus push the handler here. + */ + frame = (struct rt_sigframe __user *) ((unsigned long) frame - + sizeof(unsigned long)); + err |= __put_user((unsigned long)ksig->ka.sa.sa_handler, + (unsigned long *)frame); +#endif + if (err) return err; @@ -442,9 +461,25 @@ SYSCALL_DEFINE0(rt_sigreturn) unsigned long sp = PT_REGS_SP(¤t->thread.regs); struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(sp - sizeof(long)); - struct ucontext __user *uc = &frame->uc; + struct ucontext __user *uc; sigset_t set; +#ifndef CONFIG_MMU + /** + * we enter here with: + * + * __restore_rt: + * mov $15, %rax + * call *%rax (translated from syscall) + * + * (code is from musl libc) + * so, stack needs to be popped of "call"ed address before + * looking at rt_sigframe. + */ + frame = (struct rt_sigframe __user *)((unsigned long)frame + sizeof(long)); +#endif + uc = &frame->uc; + if (copy_from_user(&set, &uc->uc_sigmask, sizeof(set))) goto segfault; -- 2.43.0