diff options
author | Alex Shi <alex.shi@linaro.org> | 2015-05-21 10:02:25 +0800 |
---|---|---|
committer | Alex Shi <alex.shi@linaro.org> | 2015-05-21 10:02:25 +0800 |
commit | baf41996d83a6ecd270be42ca1870592827fb30d (patch) | |
tree | fd59f83bdadfc43ed9b18b4ef5dc5f01f715d1fd /arch/arc/kernel/signal.c | |
parent | 8a95b5736641a5cd3996e4fe4c0af6ea05cfa86c (diff) | |
parent | b5bac1f597ae5669dee0d2ae927b8ded0b8f6b34 (diff) |
Merge tag 'v3.10.79' into linux-linaro-lsk-v3.10
This is the 3.10.79 stable release
Diffstat (limited to 'arch/arc/kernel/signal.c')
-rw-r--r-- | arch/arc/kernel/signal.c | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index 7e95e1a86510..6763654239a2 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -131,6 +131,15 @@ SYSCALL_DEFINE0(rt_sigreturn) /* Don't restart from sigreturn */ syscall_wont_restart(regs); + /* + * Ensure that sigreturn always returns to user mode (in case the + * regs saved on user stack got fudged between save and sigreturn) + * Otherwise it is easy to panic the kernel with a custom + * signal handler and/or restorer which clobberes the status32/ret + * to return to a bogus location in kernel mode. + */ + regs->status32 |= STATUS_U_MASK; + return regs->r0; badframe: @@ -234,8 +243,11 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info, /* * handler returns using sigreturn stub provided already by userpsace + * If not, nuke the process right away */ - BUG_ON(!(ka->sa.sa_flags & SA_RESTORER)); + if(!(ka->sa.sa_flags & SA_RESTORER)) + return 1; + regs->blink = (unsigned long)ka->sa.sa_restorer; /* User Stack for signal handler will be above the frame just carved */ @@ -302,12 +314,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, struct pt_regs *regs) { sigset_t *oldset = sigmask_to_save(); - int ret; + int failed; /* Set up the stack frame */ - ret = setup_rt_frame(sig, ka, info, oldset, regs); + failed = setup_rt_frame(sig, ka, info, oldset, regs); - if (ret) + if (failed) force_sigsegv(sig, current); else signal_delivered(sig, info, ka, regs, 0); |