kqueue: kq_lock is needed when updating kn_status
authorvisa <visa@openbsd.org>
Wed, 16 Jun 2021 14:26:30 +0000 (14:26 +0000)
committervisa <visa@openbsd.org>
Wed, 16 Jun 2021 14:26:30 +0000 (14:26 +0000)
The kn_status field of struct knote is part of kqueue's internal state.
When kn_status is being updated, kq_lock has to be locked. This is true
even with MP-unsafe event filters.

OK mpi@

sys/kern/kern_event.c
sys/sys/event.h

index 48d3400..ed2b1d1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_event.c,v 1.166 2021/06/11 04:29:54 visa Exp $   */
+/*     $OpenBSD: kern_event.c,v 1.167 2021/06/16 14:26:30 visa Exp $   */
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -328,10 +328,15 @@ filt_procattach(struct knote *kn)
 void
 filt_procdetach(struct knote *kn)
 {
+       struct kqueue *kq = kn->kn_kq;
        struct process *pr = kn->kn_ptr.p_process;
-       int s;
+       int s, status;
+
+       mtx_enter(&kq->kq_lock);
+       status = kn->kn_status;
+       mtx_leave(&kq->kq_lock);
 
-       if (kn->kn_status & KN_DETACHED)
+       if (status & KN_DETACHED)
                return;
 
        s = splhigh();
@@ -342,6 +347,7 @@ filt_procdetach(struct knote *kn)
 int
 filt_proc(struct knote *kn, long hint)
 {
+       struct kqueue *kq = kn->kn_kq;
        u_int event;
 
        /*
@@ -363,8 +369,11 @@ filt_proc(struct knote *kn, long hint)
                struct process *pr = kn->kn_ptr.p_process;
                int s;
 
-               s = splhigh();
+               mtx_enter(&kq->kq_lock);
                kn->kn_status |= KN_DETACHED;
+               mtx_leave(&kq->kq_lock);
+
+               s = splhigh();
                kn->kn_flags |= (EV_EOF | EV_ONESHOT);
                kn->kn_data = W_EXITCODE(pr->ps_xexit, pr->ps_xsig);
                klist_remove_locked(&pr->ps_klist, kn);
@@ -391,7 +400,7 @@ filt_proc(struct knote *kn, long hint)
                kev.fflags = kn->kn_sfflags;
                kev.data = kn->kn_id;                   /* parent */
                kev.udata = kn->kn_udata;               /* preserve udata */
-               error = kqueue_register(kn->kn_kq, &kev, NULL);
+               error = kqueue_register(kq, &kev, NULL);
                if (error)
                        kn->kn_fflags |= NOTE_TRACKERR;
        }
index 27e9d97..4df0d93 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: event.h,v 1.55 2021/06/02 13:56:28 visa Exp $ */
+/*     $OpenBSD: event.h,v 1.56 2021/06/16 14:26:30 visa Exp $ */
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -228,6 +228,7 @@ struct filterops {
  * Locking:
  *     I       immutable after creation
  *     o       object lock
+ *     q       kn_kq->kq_lock
  */
 struct knote {
        SLIST_ENTRY(knote)      kn_link;        /* for fd */
@@ -235,7 +236,7 @@ struct knote {
        TAILQ_ENTRY(knote)      kn_tqe;
        struct                  kqueue *kn_kq;  /* [I] which queue we are on */
        struct                  kevent kn_kevent;
-       int                     kn_status;
+       int                     kn_status;      /* [q] */
        int                     kn_sfflags;     /* [o] saved filter flags */
        __int64_t               kn_sdata;       /* [o] saved data field */
        union {