aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2018-11-21 17:32:09 +0000
committerMark Johnston <markj@FreeBSD.org>2018-11-21 17:32:09 +0000
commitd5e494fee4daf42e505af339677642ca507c9839 (patch)
tree361ec7a25400d0c99f33415810ce0fccc6f574cf
parent45aecd0422ae83e9de44e9412208e3fdf3474aab (diff)
downloadsrc-d5e494fee4daf42e505af339677642ca507c9839.tar.gz
src-d5e494fee4daf42e505af339677642ca507c9839.zip
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
Notes
Notes: svn path=/head/; revision=340734
-rw-r--r--sys/kern/kern_event.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index fa59153f77e6..bf725d4d3fb3 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -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