All of lore.kernel.org
 help / color / mirror / Atom feed
* [possible bug] missed wakeup in do_sigtimedwait()?
@ 2021-09-04 14:42 Al Viro
  2021-09-04 16:59 ` Linus Torvalds
  0 siblings, 1 reply; 5+ messages in thread
From: Al Viro @ 2021-09-04 14:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel

do_sigtimedwait():
        spin_lock_irq(&tsk->sighand->siglock);
        sig = dequeue_signal(tsk, &mask, info);
nope, nothing posted yet
        if (!sig && timeout) {
                /*
                 * None ready, temporarily unblock those we're interested
                 * while we are sleeping in so that we'll be awakened when
                 * they arrive. Unblocking is always fine, we can avoid
                 * set_current_blocked().
                 */
                tsk->real_blocked = tsk->blocked;
                sigandsets(&tsk->blocked, &tsk->blocked, &mask);
                recalc_sigpending();
                spin_unlock_irq(&tsk->sighand->siglock);
... and now somebody sends us a signal.  signal_wake_up() does nothing,
since we are still in TASK_RUNNING at that point

                __set_current_state(TASK_INTERRUPTIBLE);
                ret = freezable_schedule_hrtimeout_range(to, tsk->timer_slack_ns,
                                                         HRTIMER_MODE_REL);
... and we go to sleep for the duration of timeout or until the next
signal to arrive.

                spin_lock_irq(&tsk->sighand->siglock);
                __set_task_blocked(tsk, &tsk->real_blocked);
                sigemptyset(&tsk->real_blocked);
                sig = dequeue_signal(tsk, &mask, info);
... now we finally dequeue the sucker that had been pending through the
entire timeout period.

        }
        spin_unlock_irq(&tsk->sighand->siglock);

Looks like that __set_current_state() should've been done before dropping
the siglock.  Am I missing something subtle here?  It's not a terribly
wide window, but it's not impossible to hit e.g. on KVM and it does look
like a missed wakeup problem...  For that matter, spin_unlock_irq() might
run irq handlers, so it's not impossible to hit on the real hardware either.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [possible bug] missed wakeup in do_sigtimedwait()?
  2021-09-04 14:42 [possible bug] missed wakeup in do_sigtimedwait()? Al Viro
@ 2021-09-04 16:59 ` Linus Torvalds
  2021-09-04 17:12   ` Linus Torvalds
  0 siblings, 1 reply; 5+ messages in thread
From: Linus Torvalds @ 2021-09-04 16:59 UTC (permalink / raw)
  To: Al Viro; +Cc: Linux Kernel Mailing List

On Sat, Sep 4, 2021 at 7:45 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Looks like that __set_current_state() should've been done before dropping
> the siglock.  Am I missing something subtle here?

I agree, that seems like a bug, and your fix seems the trivially correct thing.

The bug goes back at least before ther bk history (in 2002).
Presumably since the introduction of the system call.

         Linus

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [possible bug] missed wakeup in do_sigtimedwait()?
  2021-09-04 16:59 ` Linus Torvalds
@ 2021-09-04 17:12   ` Linus Torvalds
  2021-09-04 18:11     ` Al Viro
  0 siblings, 1 reply; 5+ messages in thread
From: Linus Torvalds @ 2021-09-04 17:12 UTC (permalink / raw)
  To: Al Viro; +Cc: Linux Kernel Mailing List

On Sat, Sep 4, 2021 at 9:59 AM Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> I agree, that seems like a bug, and your fix seems the trivially correct thing.

Oh, never mind.  Signals are special.

Why?

Because TASK_INTERRUPTIBLE is special, and schedule() will check for
"am I trying to sleep while a signal is pending" and will never
actually sleep.

So you can't have missed wakeups from signals, because this sequence
is perfectly ok, by design:

 - signal comes in and is pending

 - we set TASK_INTERRUPTIBLE

 - we are thinking about something *entirely* different, like looking
at a pipe being emty

 - we schedule()

and the pending signal will just mean that we never go to sleep.

It's designed that way exactly so that people who have interruptible
sleeps don't need to think about signals at all - they can concentrate
on doing their own thing, and then do the "signal_pending()" check at
any point without caring.

This has always been true, I had just swapped out that logic from memory.

             Linus

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [possible bug] missed wakeup in do_sigtimedwait()?
  2021-09-04 17:12   ` Linus Torvalds
@ 2021-09-04 18:11     ` Al Viro
  2021-09-04 18:21       ` Linus Torvalds
  0 siblings, 1 reply; 5+ messages in thread
From: Al Viro @ 2021-09-04 18:11 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Linux Kernel Mailing List

On Sat, Sep 04, 2021 at 10:12:09AM -0700, Linus Torvalds wrote:
> On Sat, Sep 4, 2021 at 9:59 AM Linus Torvalds
> <torvalds@linux-foundation.org> wrote:
> >
> > I agree, that seems like a bug, and your fix seems the trivially correct thing.
> 
> Oh, never mind.  Signals are special.
> 
> Why?
> 
> Because TASK_INTERRUPTIBLE is special, and schedule() will check for
> "am I trying to sleep while a signal is pending" and will never
> actually sleep.
> 
> So you can't have missed wakeups from signals, because this sequence
> is perfectly ok, by design:
> 
>  - signal comes in and is pending
> 
>  - we set TASK_INTERRUPTIBLE
> 
>  - we are thinking about something *entirely* different, like looking
> at a pipe being emty
> 
>  - we schedule()
> 
> and the pending signal will just mean that we never go to sleep.
> 
> It's designed that way exactly so that people who have interruptible
> sleeps don't need to think about signals at all - they can concentrate
> on doing their own thing, and then do the "signal_pending()" check at
> any point without caring.

Thanks.  AFAICS, it's this logics in __schedule():
                if (signal_pending_state(prev_state, prev)) {
                        WRITE_ONCE(prev->__state, TASK_RUNNING);
IOW, TASK_INTERRUPTIBLE with signal_pending() or TASK_WAKEKILL with
pending SIGKILL.  OK...

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [possible bug] missed wakeup in do_sigtimedwait()?
  2021-09-04 18:11     ` Al Viro
@ 2021-09-04 18:21       ` Linus Torvalds
  0 siblings, 0 replies; 5+ messages in thread
From: Linus Torvalds @ 2021-09-04 18:21 UTC (permalink / raw)
  To: Al Viro; +Cc: Linux Kernel Mailing List

On Sat, Sep 4, 2021 at 11:13 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Thanks.  AFAICS, it's this logics in __schedule():
>                 if (signal_pending_state(prev_state, prev)) {
>                         WRITE_ONCE(prev->__state, TASK_RUNNING);

Exactly. As you note, it also handles the WAKEKILL case which is
basically equivalent (just for the "I can't take normal signals, but
I'm ok dying" case)

            Linus

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-09-04 18:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-04 14:42 [possible bug] missed wakeup in do_sigtimedwait()? Al Viro
2021-09-04 16:59 ` Linus Torvalds
2021-09-04 17:12   ` Linus Torvalds
2021-09-04 18:11     ` Al Viro
2021-09-04 18:21       ` Linus Torvalds

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.