git: d795c753e262 - main - kevent: Hold the knlist mutex when invoking f_event(NOTE_FORK)

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 18 Nov 2025 16:24:47 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=d795c753e262b97a93dc353aa66b858e1b1969d1

commit d795c753e262b97a93dc353aa66b858e1b1969d1
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-11-18 14:22:04 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-11-18 16:24:21 +0000

    kevent: Hold the knlist mutex when invoking f_event(NOTE_FORK)
    
    In general f_event is supposed to be called with the knlist mutex held,
    so lock it earlier to follow this protocol.  Also make sure that the
    update to kn_fflags is synchronized.
    
    Lock the kqueue itself earlier in the case where the knote is activated,
    to avoid locking and unlocking the kqueue twice.
    
    PR:             291005
    Reported by:    Qiu-ji Chen <chenqiuji666@gmail.com>
    Reviewed by:    kib
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D53762
---
 sys/kern/kern_event.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 1baa24d278bf..a48408fd482a 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -627,12 +627,20 @@ knote_fork(struct knlist *list, int pid)
 		kev.data = kn->kn_id;		/* parent */
 		kev.udata = kn->kn_kevent.udata;/* preserve udata */
 		error = kqueue_register(kq, &kev, NULL, M_NOWAIT);
+
+		/*
+		 * Serialize updates to the kn_kevent fields with threads
+		 * scanning the queue.
+		 */
+		list->kl_lock(list->kl_lockarg);
 		if (error)
 			kn->kn_fflags |= NOTE_TRACKERR;
-		if (kn->kn_fop->f_event(kn, NOTE_FORK))
-			KNOTE_ACTIVATE(kn, 0);
-		list->kl_lock(list->kl_lockarg);
-		KQ_LOCK(kq);
+		if (kn->kn_fop->f_event(kn, NOTE_FORK)) {
+			KQ_LOCK(kq);
+			KNOTE_ACTIVATE(kn, 1);
+		} else {
+			KQ_LOCK(kq);
+		}
 		kn_leave_flux(kn);
 		KQ_UNLOCK_FLUX(kq);
 	}