diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-18 04:42:19 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:45:50 -0500 |
commit | 55bb9480f9159b229ac3c3454c97b62d1e0a7e80 (patch) | |
tree | 4de8a97445bb41f250cdadba78169d0463bd244b /arch/arc | |
parent | 5c39c0ab5e862cf71cda1fc39a5cedd4e2f18c6e (diff) |
ARC: [Review] Prevent incorrect syscall restarts
Per Al Viro's "signals for dummies" https://lkml.org/lkml/2012/12/6/366
there are 3 golden rules for (not) restarting syscalls:
" What we need to guarantee is
* restarts do not happen on signals caught in interrupts or exceptions
* restarts do not happen on signals caught in sigreturn()
* restart should happen only once, even if we get through do_signal()
many times."
ARC Port already handled #1, this patch fixes #2 and #3.
We use the additional state in pt_regs->orig_r8 to ckh if restarting
has already been done once.
Thanks to Al Viro for spotting this.
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/include/asm/ptrace.h | 3 | ||||
-rw-r--r-- | arch/arc/kernel/signal.c | 12 |
2 files changed, 11 insertions, 4 deletions
diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 3ec89f467ac3..a98957a95fb3 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h | |||
@@ -100,6 +100,9 @@ struct callee_regs { | |||
100 | #define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) | 100 | #define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) |
101 | #define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) | 101 | #define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) |
102 | 102 | ||
103 | #define syscall_wont_restart(regs) (regs->event |= orig_r8_IS_SCALL_RESTARTED) | ||
104 | #define syscall_restartable(regs) !(regs->event & orig_r8_IS_SCALL_RESTARTED) | ||
105 | |||
103 | #define current_pt_regs() \ | 106 | #define current_pt_regs() \ |
104 | ({ \ | 107 | ({ \ |
105 | /* open-coded current_thread_info() */ \ | 108 | /* open-coded current_thread_info() */ \ |
diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index ab8ed5a55461..ee6ef2f60a28 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c | |||
@@ -128,6 +128,9 @@ SYSCALL_DEFINE0(rt_sigreturn) | |||
128 | if (restore_altstack(&sf->uc.uc_stack)) | 128 | if (restore_altstack(&sf->uc.uc_stack)) |
129 | goto badframe; | 129 | goto badframe; |
130 | 130 | ||
131 | /* Don't restart from sigreturn */ | ||
132 | syscall_wont_restart(regs); | ||
133 | |||
131 | return regs->r0; | 134 | return regs->r0; |
132 | 135 | ||
133 | badframe: | 136 | badframe: |
@@ -318,13 +321,13 @@ void do_signal(struct pt_regs *regs) | |||
318 | 321 | ||
319 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 322 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
320 | 323 | ||
321 | /* Are we from a system call? */ | 324 | restart_scall = in_syscall(regs) && syscall_restartable(regs); |
322 | restart_scall = in_syscall(regs); | ||
323 | 325 | ||
324 | if (signr > 0) { | 326 | if (signr > 0) { |
325 | if (restart_scall) | 327 | if (restart_scall) { |
326 | arc_restart_syscall(&ka, regs); | 328 | arc_restart_syscall(&ka, regs); |
327 | 329 | syscall_wont_restart(regs); /* No more restarts */ | |
330 | } | ||
328 | handle_signal(signr, &ka, &info, regs); | 331 | handle_signal(signr, &ka, &info, regs); |
329 | return; | 332 | return; |
330 | } | 333 | } |
@@ -339,6 +342,7 @@ void do_signal(struct pt_regs *regs) | |||
339 | regs->r8 = __NR_restart_syscall; | 342 | regs->r8 = __NR_restart_syscall; |
340 | regs->ret -= 4; | 343 | regs->ret -= 4; |
341 | } | 344 | } |
345 | syscall_wont_restart(regs); /* No more restarts */ | ||
342 | } | 346 | } |
343 | 347 | ||
344 | /* If there's no signal to deliver, restore the saved sigmask back */ | 348 | /* If there's no signal to deliver, restore the saved sigmask back */ |