* [PATCH] PPC32: cancel syscall restart on signal delivery
@ 2003-11-15 0:04 Paul Mackerras
2003-11-15 0:21 ` Linus Torvalds
0 siblings, 1 reply; 10+ messages in thread
From: Paul Mackerras @ 2003-11-15 0:04 UTC (permalink / raw)
To: torvalds; +Cc: trini, benh, linux-kernel
Linus, please apply.
This patch ensures that the PPC kernel cancels any pending restarted
system call when it delivers a signal. This is the PPC counterpart of
the change that has recently gone into i386 and other architectures.
BTW, do we have a test program that triggers the bug that this fixes?
Thanks,
Paul.
diff -urN linux-2.5/arch/ppc/kernel/signal.c pmac-2.5/arch/ppc/kernel/signal.c
--- linux-2.5/arch/ppc/kernel/signal.c 2003-09-27 19:46:43.000000000 +1000
+++ pmac-2.5/arch/ppc/kernel/signal.c 2003-11-14 23:08:22.000000000 +1100
@@ -569,10 +569,6 @@
regs->result = -EINTR;
regs->gpr[3] = EINTR;
/* note that the cr0.SO bit is already set */
- /* clear any restart function that was set */
- if (ret == ERESTART_RESTARTBLOCK)
- current_thread_info()->restart_block.fn
- = do_no_restart_syscall;
} else {
regs->nip -= 4; /* Back up & retry system call */
regs->result = 0;
@@ -587,6 +583,9 @@
if (signr == 0)
return 0; /* no signals delivered */
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size
&& !on_sig_stack(regs->gpr[1]))
newsp = current->sas_ss_sp + current->sas_ss_size;
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 0:04 [PATCH] PPC32: cancel syscall restart on signal delivery Paul Mackerras @ 2003-11-15 0:21 ` Linus Torvalds 2003-11-15 0:31 ` Linus Torvalds 2003-11-15 2:59 ` Paul Mackerras 0 siblings, 2 replies; 10+ messages in thread From: Linus Torvalds @ 2003-11-15 0:21 UTC (permalink / raw) To: Paul Mackerras; +Cc: trini, benh, linux-kernel On Sat, 15 Nov 2003, Paul Mackerras wrote: > > BTW, do we have a test program that triggers the bug that this fixes? No. In fact, it's an _incredibly_ tiny race, because the old code that only handled it at the return path of the system call that got interrupted would catch the thing in all cases _except_ if the system (a) decided to restart the system call (b) got an interrupt _after_ the return to user mode but _before_ the system call instruction itself actually did the restart (c) this interrupt caused a new signal with a handler (different from the one that caused the restart, since that one by definition had no handler) to the same program. On x86, for the old calling conventions (ie "int 0x80"), that meant that the interrupt window was literally a single instruction. For the new one ("sysenter") it was two instructions. So you literally need a minimum of two different signals, and a incredibly tight window. To make it even worse, even if the above incredibly unlikely thing actually _happens_, most of the time it wouldn't really matter. The worst case schenario is that the signal handler itself does a system call that needs restarting, in which case on signal handler return we now restart the _wrong_ system call once we return. But a more likely schenario is that we restart the right system call after the signal handler has run, rather than returning EINTR. Quite frankly, the only reason I even thought about it was due to the clock_nanosleep() patch to fix the posix timer restart code. Which just made me trace the sequence again in my head. Linus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 0:21 ` Linus Torvalds @ 2003-11-15 0:31 ` Linus Torvalds 2003-11-15 2:59 ` Paul Mackerras 1 sibling, 0 replies; 10+ messages in thread From: Linus Torvalds @ 2003-11-15 0:31 UTC (permalink / raw) To: Paul Mackerras; +Cc: trini, benh, linux-kernel On Fri, 14 Nov 2003, Linus Torvalds wrote: > > On x86, for the old calling conventions (ie "int 0x80"), that meant that > the interrupt window was literally a single instruction. For the new one > ("sysenter") it was two instructions. Actually, three instructions for "sysenter": the "movl %esp,%ebp" and the actual "sysenter" instruction itself are the main restart sequence, but to make it look the same as the legacy code from a kernel restart standpoint there is also a short backwards jump there. Anyway, you can't even trigger it with the single-step flag, since the kernel will disable single-stepping in sysenter (and it only gets re-enabled once the system call is done - including retries). Since it literally requires an external interrupt to happen, it would be pretty hard to create a test-case for. Maybe a dual-CPU setup with the other CPU sending a signal (and the interrupt is the resultant IPI) at just the right moment.. Linus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 0:21 ` Linus Torvalds 2003-11-15 0:31 ` Linus Torvalds @ 2003-11-15 2:59 ` Paul Mackerras 2003-11-15 3:11 ` Linus Torvalds 1 sibling, 1 reply; 10+ messages in thread From: Paul Mackerras @ 2003-11-15 2:59 UTC (permalink / raw) To: Linus Torvalds; +Cc: linux-kernel Linus Torvalds writes: > On Sat, 15 Nov 2003, Paul Mackerras wrote: > > > > BTW, do we have a test program that triggers the bug that this fixes? > > No. In fact, it's an _incredibly_ tiny race, because the old code that > only handled it at the return path of the system call that got interrupted > would catch the thing in all cases _except_ if the system > > (a) decided to restart the system call > (b) got an interrupt _after_ the return to user mode but _before_ the > system call instruction itself actually did the restart > (c) this interrupt caused a new signal with a handler (different from the > one that caused the restart, since that one by definition had no > handler) to the same program. I just had another look at the x86 syscall exit code. It looks to me as though you have interrupts disabled all the way from before you check current_thread_info()->flags, through the call to do_signal, until you return to userspace. Also, you deliver one signal and then return to userspace even if there are more signals pending. I could be wrong about either or both assertions, of course, my x86 assembler isn't particularly strong. (Having interrupts disabled during do_signal is interesting, given that its subroutines call __put_user and friends. :) On PPC, I disable interrupts before checking the thread_info flags, but then reenable them before calling do_signal. Then after do_signal returns, I check the thread_info flags again (having first disabled interrupts), so if there is another signal pending, it will get delivered before we return to userspace. Thus I think the race was possibly a little wider on PPC than on x86: we didn't have to get back to userspace, the interrupt could happen during do_signal. However, I think the race window was actually wider on x86 than you seem to imply, since the external interrupt could become pending any time from when you disable interrupts before checking the thread_info flags until you return to userspace, and still cause the problem. Still, either way it's a pretty narrow race. > To make it even worse, even if the above incredibly unlikely thing > actually _happens_, most of the time it wouldn't really matter. The worst > case schenario is that the signal handler itself does a system call that > needs restarting, in which case on signal handler return we now restart > the _wrong_ system call once we return. Now you have me scared, because I can't see where the restart_syscall system call resets current_thread_info()->restart_block.fn to do_no_restart_syscall. Now, when we get an ignored signal during a restartable system call, the signal code sets up userspace to do a restart_syscall system call. If we get the race and then deliver a signal to the process, we don't undo the changes to userspace, we just arrange that when it does the restart_syscall call, it will give the same effect as if the original system call had returned -EINTR. Now if the signal handler does a restartable system call, which ends needing to be restarted, that will undo the change that we made on signal delivery where we set restart_block.fn = do_no_restart_syscall. In other words, we will return from the signal handler with restart_block.fn set to something else, and promptly proceed to restart the wrong syscall. Am I missing something? Perhaps we should reset restart_block.fn in sys_{,rt_}sigreturn, or possibly in sys_restart_syscall. Paul. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 2:59 ` Paul Mackerras @ 2003-11-15 3:11 ` Linus Torvalds 2003-11-15 3:25 ` Linus Torvalds 2003-11-15 5:03 ` Paul Mackerras 0 siblings, 2 replies; 10+ messages in thread From: Linus Torvalds @ 2003-11-15 3:11 UTC (permalink / raw) To: Paul Mackerras; +Cc: linux-kernel On Sat, 15 Nov 2003, Paul Mackerras wrote: > > (Having interrupts disabled during do_signal is interesting, given > that its subroutines call __put_user and friends. :) Actually, look closer. We don't do that. Why? Check out get_signal_to_deliver(). And grok the absolute horridness. > Thus I think the race was possibly a little wider on PPC than on x86: > we didn't have to get back to userspace, the interrupt could happen > during do_signal. No. If it happens during do_signal() (on x86 or ppc), we'd just not _handle_ the signal at all. We'd return to user space, restart the system call, and handle the signal as the restarted system call is returning. No bug. In short, even on ppc, the window _literally_ is "after we've returned, but before we restart". > Now you have me scared, because I can't see where the restart_syscall > system call resets current_thread_info()->restart_block.fn to > do_no_restart_syscall. It doesn't. The rule is: the restart_block is _only_ meaningful if you return -ERESTART_BLOCK. So at any other time it contains stale data. > Am I missing something? Perhaps we should reset restart_block.fn in > sys_{,rt_}sigreturn, or possibly in sys_restart_syscall. You're missing that the only thing that ever looks at restart_block is the code that is inside the signal handling of ERESTART_BLOCK. The bug was that sometimes we had _already_ done that ERESTART_BLOCK handling (correctly), but then basically "aborted" (thanks to another signal) before the restart had actually taken effect. And that race literally is only in user mode. Linus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 3:11 ` Linus Torvalds @ 2003-11-15 3:25 ` Linus Torvalds 2003-11-15 5:03 ` Paul Mackerras 1 sibling, 0 replies; 10+ messages in thread From: Linus Torvalds @ 2003-11-15 3:25 UTC (permalink / raw) To: Paul Mackerras; +Cc: linux-kernel On Fri, 14 Nov 2003, Linus Torvalds wrote: > > Why? Check out get_signal_to_deliver(). And grok the absolute horridness. Btw, don't get me wrong - I'm not proud about it. I only noticed how horrid it was of x86 to depend on this behaviour in do_signal() about a week ago, and since it's not a bug I'm not fixing it for 2.6.0. It may not be pretty, but it works. Linus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 3:11 ` Linus Torvalds 2003-11-15 3:25 ` Linus Torvalds @ 2003-11-15 5:03 ` Paul Mackerras 2003-11-15 5:14 ` Linus Torvalds 1 sibling, 1 reply; 10+ messages in thread From: Paul Mackerras @ 2003-11-15 5:03 UTC (permalink / raw) To: Linus Torvalds; +Cc: linux-kernel Linus Torvalds writes: > Why? Check out get_signal_to_deliver(). And grok the absolute horridness. Yes, that is pretty special, isn't it. :) > The rule is: the restart_block is _only_ meaningful if you return > -ERESTART_BLOCK. So at any other time it contains stale data. > > > Am I missing something? Perhaps we should reset restart_block.fn in > > sys_{,rt_}sigreturn, or possibly in sys_restart_syscall. > > You're missing that the only thing that ever looks at restart_block is the > code that is inside the signal handling of ERESTART_BLOCK. ... and sys_restart_syscall(). If your statement was true, why was it so important to reset restart_block.fn when we deliver a signal? Seems to me that we can get into a situation where we are in a signal handler, and the interrupted state has (1) eip/nip pointing at a system call instruction and (2) the syscall number register (eax/r0) containing __NR_restart_syscall. Now, if we get into this state we will initially have restart_block.fn == do_no_restart_syscall. But that can get changed by the signal handler. Now, when we resume that context we will call sys_restart_syscall which will call restart_block.fn. Which won't necessarily still point to do_no_restart_syscall. So I still think we have a problem. Paul. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 5:03 ` Paul Mackerras @ 2003-11-15 5:14 ` Linus Torvalds 2003-11-15 5:47 ` Linus Torvalds 2003-11-15 6:26 ` Paul Mackerras 0 siblings, 2 replies; 10+ messages in thread From: Linus Torvalds @ 2003-11-15 5:14 UTC (permalink / raw) To: Paul Mackerras; +Cc: linux-kernel On Sat, 15 Nov 2003, Paul Mackerras wrote: > > Now, when we resume that context we will call sys_restart_syscall > which will call restart_block.fn. Which won't necessarily still point > to do_no_restart_syscall. So I still think we have a problem. Excellent point. We're actually much better off resetting it at signal return. Which should make all other resets unnecessary. Yes, you can get out of a signal by using longjmp, but that doesn't matter: you can't longjump to a restart call (well, you can, but only if the user literally tries to do the restart by hand, ie he _intended_ to do it). So the _proper_ fix (for x86) should be as appended. Agreed? Linus ---- ===== arch/i386/kernel/signal.c 1.33 vs edited ===== --- 1.33/arch/i386/kernel/signal.c Tue Nov 11 21:18:46 2003 +++ edited/arch/i386/kernel/signal.c Fri Nov 14 21:13:09 2003 @@ -132,6 +132,9 @@ { unsigned int err = 0; + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + #define COPY(x) err |= __get_user(regs->x, &sc->x) #define COPY_SEG(seg) \ @@ -503,9 +506,6 @@ struct pt_regs * regs) { struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - - /* Always make any pending restarted system calls return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; /* Are we from a system call? */ if (regs->orig_eax >= 0) { ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 5:14 ` Linus Torvalds @ 2003-11-15 5:47 ` Linus Torvalds 2003-11-15 6:26 ` Paul Mackerras 1 sibling, 0 replies; 10+ messages in thread From: Linus Torvalds @ 2003-11-15 5:47 UTC (permalink / raw) To: Paul Mackerras; +Cc: linux-kernel On Fri, 14 Nov 2003, Linus Torvalds wrote: > > So the _proper_ fix (for x86) should be as appended. Agreed? Btw, while this won't oops the machine or anything like that, I consider bugs like this where the kernel just silently does the wrong thing to be potentially even more serious, since they cause endless head-scratching and the "flaky" reports. So if somebody can come up with a way to test this sanely, it would be nice (thinking about it I suspect that you _should_ be able to trigger this on x86 by using the old "int 0x80" syscalls together with setting TF in eflags). Linus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] PPC32: cancel syscall restart on signal delivery 2003-11-15 5:14 ` Linus Torvalds 2003-11-15 5:47 ` Linus Torvalds @ 2003-11-15 6:26 ` Paul Mackerras 1 sibling, 0 replies; 10+ messages in thread From: Paul Mackerras @ 2003-11-15 6:26 UTC (permalink / raw) To: Linus Torvalds; +Cc: linux-kernel Linus Torvalds writes: > Excellent point. We're actually much better off resetting it at signal > return. > > Which should make all other resets unnecessary. Yes. > Yes, you can get out of a signal by using longjmp, but that doesn't > matter: you can't longjump to a restart call (well, you can, but only if > the user literally tries to do the restart by hand, ie he _intended_ to do > it). > > So the _proper_ fix (for x86) should be as appended. Agreed? Agreed. Here is the patch for PPC. Thanks, Paul. diff -urN linux-2.5/arch/ppc/kernel/signal.c pmac-2.5/arch/ppc/kernel/signal.c --- linux-2.5/arch/ppc/kernel/signal.c 2003-11-15 16:55:18.618797320 +1100 +++ pmac-2.5/arch/ppc/kernel/signal.c 2003-11-15 17:09:53.145849000 +1100 @@ -418,6 +418,9 @@ { struct rt_sigframe __user *rt_sf; + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + rt_sf = (struct rt_sigframe __user *) (regs->gpr[1] + __SIGNAL_FRAMESIZE + 16); if (verify_area(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe))) @@ -513,6 +516,9 @@ struct mcontext __user *sr; sigset_t set; + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); if (copy_from_user(&sigctx, sc, sizeof(sigctx))) goto badframe; @@ -583,9 +589,6 @@ if (signr == 0) return 0; /* no signals delivered */ - /* Always make any pending restarted system calls return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; - if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size && !on_sig_stack(regs->gpr[1])) newsp = current->sas_ss_sp + current->sas_ss_size; ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2003-11-15 6:23 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2003-11-15 0:04 [PATCH] PPC32: cancel syscall restart on signal delivery Paul Mackerras 2003-11-15 0:21 ` Linus Torvalds 2003-11-15 0:31 ` Linus Torvalds 2003-11-15 2:59 ` Paul Mackerras 2003-11-15 3:11 ` Linus Torvalds 2003-11-15 3:25 ` Linus Torvalds 2003-11-15 5:03 ` Paul Mackerras 2003-11-15 5:14 ` Linus Torvalds 2003-11-15 5:47 ` Linus Torvalds 2003-11-15 6:26 ` Paul Mackerras
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®