Author: markj Date: Wed Nov 21 17:32:09 2018 New Revision: 340734 URL: https://svnweb.freebsd.org/changeset/base/340734
Log: 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 <s...@efficientip.com> Reviewed by: kib MFC after: 3 days Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D18060 Modified: head/sys/kern/kern_event.c Modified: head/sys/kern/kern_event.c ============================================================================== --- head/sys/kern/kern_event.c Wed Nov 21 17:28:10 2018 (r340733) +++ head/sys/kern/kern_event.c Wed Nov 21 17:32:09 2018 (r340734) @@ -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 _______________________________________________ svn-src-head@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"