Avoid unsynchronized updates to kn_status.
kn_status is protected by the kqueue's lock, but we were updating it without the kqueue lock held. For EVFILT_TIMER knotes, there is no knlist lock, so the knote activation could occur during the kn_status update and result in KN_QUEUED being lost, in which case we'd enqueue an already-enqueued knote, corrupting the queue. Fix the problem by setting or clearing KN_DISABLED before dropping the kqueue lock to call into the filter. KN_DISABLED is used only by the core kevent code, so there is no side effect from setting it earlier. Reported and tested by: Sylvain GALLIANO <sg@efficientip.com> Reviewed by: kib MFC after: 3 days Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D18060
This commit is contained in:
parent
6994e92830
commit
2e57d41f44
@ -1533,6 +1533,8 @@ findkn:
|
||||
kn->kn_kevent.flags &= ~(EV_ADD | EV_DELETE |
|
||||
EV_ENABLE | EV_DISABLE | EV_FORCEONESHOT);
|
||||
kn->kn_status = KN_DETACHED;
|
||||
if ((kev->flags & EV_DISABLE) != 0)
|
||||
kn->kn_status |= KN_DISABLED;
|
||||
kn_enter_flux(kn);
|
||||
|
||||
error = knote_attach(kn, kq);
|
||||
@ -1568,6 +1570,11 @@ findkn:
|
||||
KNOTE_ACTIVATE(kn, 1);
|
||||
}
|
||||
|
||||
if ((kev->flags & EV_ENABLE) != 0)
|
||||
kn->kn_status &= ~KN_DISABLED;
|
||||
else if ((kev->flags & EV_DISABLE) != 0)
|
||||
kn->kn_status |= KN_DISABLED;
|
||||
|
||||
/*
|
||||
* The user may change some filter values after the initial EV_ADD,
|
||||
* but doing so will not reset any filter which has already been
|
||||
@ -1585,19 +1592,17 @@ findkn:
|
||||
kn->kn_sdata = kev->data;
|
||||
}
|
||||
|
||||
done_ev_add:
|
||||
/*
|
||||
* We can get here with kn->kn_knlist == NULL. This can happen when
|
||||
* the initial attach event decides that the event is "completed"
|
||||
* already. i.e. filt_procattach is called on a zombie process. It
|
||||
* will call filt_proc which will remove it from the list, and NULL
|
||||
* already, e.g., filt_procattach() is called on a zombie process. It
|
||||
* will call filt_proc() which will remove it from the list, and NULL
|
||||
* kn_knlist.
|
||||
*
|
||||
* KN_DISABLED will be stable while the knote is in flux, so the
|
||||
* unlocked read will not race with an update.
|
||||
*/
|
||||
done_ev_add:
|
||||
if ((kev->flags & EV_ENABLE) != 0)
|
||||
kn->kn_status &= ~KN_DISABLED;
|
||||
else if ((kev->flags & EV_DISABLE) != 0)
|
||||
kn->kn_status |= KN_DISABLED;
|
||||
|
||||
if ((kn->kn_status & KN_DISABLED) == 0)
|
||||
event = kn->kn_fop->f_event(kn, 0);
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user