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/include/shared/kern_util.h | 3 +++ arch/um/kernel/trap.c | 10 ++++++++ arch/um/os-Linux/signal.c | 18 ++++++++++++++- arch/x86/um/signal.c | 37 +++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/arch/um/include/shared/kern_util.h b/arch/um/include/shared/kern_util.h index f21dc8517538..bcc8d28279ae 100644 --- a/arch/um/include/shared/kern_util.h +++ b/arch/um/include/shared/kern_util.h @@ -62,6 +62,9 @@ extern int singlestepping(void); extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); extern void fatal_sigsegv(void) __attribute__ ((noreturn)); +#ifndef CONFIG_MMU +extern void sigsegv_post_routine(void); +#endif void um_idle_sleep(void); diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index a7519b3de4bf..b9b54e777894 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -174,6 +174,16 @@ void fatal_sigsegv(void) os_dump_core(); } +#ifndef CONFIG_MMU +void sigsegv_post_routine(void) +{ + change_sig(SIGIO, 1); + change_sig(SIGALRM, 1); + change_sig(SIGWINCH, 1); + userspace(¤t->thread.regs.regs); +} +#endif + /** * segv_handler() - the SIGSEGV handler * @sig: the signal number diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 52852018a3ad..a06622415d8f 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -36,7 +36,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); @@ -191,6 +199,7 @@ static void hard_handler(int sig, siginfo_t *si, void *p) ucontext_t *uc = p; mcontext_t *mc = &uc->uc_mcontext; unsigned long pending = 1UL << sig; + int is_segv = 0; do { int nested, bail; @@ -214,6 +223,7 @@ static void hard_handler(int sig, siginfo_t *si, void *p) while ((sig = ffs(pending)) != 0){ sig--; + is_segv = (sig == SIGSEGV) ? 1 : 0; pending &= ~(1 << sig); (*handlers[sig])(sig, (struct siginfo *)si, mc); } @@ -227,6 +237,12 @@ static void hard_handler(int sig, siginfo_t *si, void *p) if (!nested) pending = from_irq_stack(nested); } while (pending); + +#ifndef CONFIG_MMU + /* if there is SIGSEGV notified, let the userspace run w/ __noreturn */ + if (is_segv) + sigsegv_post_routine(); +#endif } void set_handler(int sig) diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c index 75087e85b6fd..b7365c75a967 100644 --- a/arch/x86/um/signal.c +++ b/arch/x86/um/signal.c @@ -371,6 +371,13 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, round_down(stack_top - sizeof(struct rt_sigframe), 16); /* Add required space for math frame */ +#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 frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size); /* Subtract 128 for a red zone and 8 for proper alignment */ @@ -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