Module Name:    src
Committed By:   thorpej
Date:           Sun Sep 26 21:29:39 UTC 2021

Modified Files:
        src/sys/kern: kern_event.c vfs_init.c vfs_syscalls.c
        src/sys/sys: event.h

Log Message:
Fix the locking around EVFILT_FS.  Previously, the code would walk the
fs_klist and take the kqueue_misc_lock inside the event callback.
However, that list can be modified by the attach and detach callbacks,
which could result in the walker stepping right off a cliff.

Instead, we give the fs_klist it's own lock, and hold it while we
call knote(), using the NOTE_SUBMIT protocol.  Also, fs_filtops
into vfs_syscalls.c so all of the locking logic is contained in one
file (there is precedence with sig_filtops).  fs_filtops is now marked
MPSAFE.


To generate a diff of this commit:
cvs rdiff -u -r1.123 -r1.124 src/sys/kern/kern_event.c
cvs rdiff -u -r1.52 -r1.53 src/sys/kern/vfs_init.c
cvs rdiff -u -r1.552 -r1.553 src/sys/kern/vfs_syscalls.c
cvs rdiff -u -r1.42 -r1.43 src/sys/sys/event.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/kern_event.c
diff -u src/sys/kern/kern_event.c:1.123 src/sys/kern/kern_event.c:1.124
--- src/sys/kern/kern_event.c:1.123	Sun Sep 26 18:13:58 2021
+++ src/sys/kern/kern_event.c	Sun Sep 26 21:29:38 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_event.c,v 1.123 2021/09/26 18:13:58 thorpej Exp $	*/
+/*	$NetBSD: kern_event.c,v 1.124 2021/09/26 21:29:38 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -59,7 +59,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.123 2021/09/26 18:13:58 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.124 2021/09/26 21:29:38 thorpej Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -108,9 +108,6 @@ static void	filt_timerexpire(void *x);
 static int	filt_timerattach(struct knote *);
 static void	filt_timerdetach(struct knote *);
 static int	filt_timer(struct knote *, long hint);
-static int	filt_fsattach(struct knote *kn);
-static void	filt_fsdetach(struct knote *kn);
-static int	filt_fs(struct knote *kn, long hint);
 static int	filt_userattach(struct knote *);
 static void	filt_userdetach(struct knote *);
 static int	filt_user(struct knote *, long hint);
@@ -163,13 +160,6 @@ static const struct filterops timer_filt
 	.f_event = filt_timer,
 };
 
-static const struct filterops fs_filtops = {
-	.f_flags = 0,
-	.f_attach = filt_fsattach,
-	.f_detach = filt_fsdetach,
-	.f_event = filt_fs,
-};
-
 static const struct filterops user_filtops = {
 	.f_flags = FILTEROP_MPSAFE,
 	.f_attach = filt_userattach,
@@ -184,7 +174,8 @@ static int	kq_calloutmax = (4 * 1024);
 #define	KN_HASHSIZE		64		/* XXX should be tunable */
 #define	KN_HASH(val, mask)	(((val) ^ (val >> 8)) & (mask))
 
-extern const struct filterops sig_filtops;
+extern const struct filterops fs_filtops;	/* vfs_syscalls.c */
+extern const struct filterops sig_filtops;	/* kern_sig.c */
 
 #define KQ_FLUX_WAKEUP(kq)	cv_broadcast(&kq->kq_cv)
 
@@ -823,45 +814,6 @@ filt_timer(struct knote *kn, long hint)
 	return rv;
 }
 
-/*
- * Filter event method for EVFILT_FS.
- */
-struct klist fs_klist = SLIST_HEAD_INITIALIZER(&fs_klist);
-
-static int
-filt_fsattach(struct knote *kn)
-{
-
-	mutex_enter(&kqueue_misc_lock);
-	kn->kn_flags |= EV_CLEAR;
-	SLIST_INSERT_HEAD(&fs_klist, kn, kn_selnext);
-	mutex_exit(&kqueue_misc_lock);
-
-	return 0;
-}
-
-static void
-filt_fsdetach(struct knote *kn)
-{
-
-	mutex_enter(&kqueue_misc_lock);
-	SLIST_REMOVE(&fs_klist, kn, knote, kn_selnext);
-	mutex_exit(&kqueue_misc_lock);
-}
-
-static int
-filt_fs(struct knote *kn, long hint)
-{
-	int rv;
-
-	mutex_enter(&kqueue_misc_lock);
-	kn->kn_fflags |= hint;
-	rv = (kn->kn_fflags != 0);
-	mutex_exit(&kqueue_misc_lock);
-
-	return rv;
-}
-
 static int
 filt_userattach(struct knote *kn)
 {

Index: src/sys/kern/vfs_init.c
diff -u src/sys/kern/vfs_init.c:1.52 src/sys/kern/vfs_init.c:1.53
--- src/sys/kern/vfs_init.c:1.52	Thu Feb  4 21:07:06 2021
+++ src/sys/kern/vfs_init.c	Sun Sep 26 21:29:38 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_init.c,v 1.52 2021/02/04 21:07:06 jdolecek Exp $	*/
+/*	$NetBSD: vfs_init.c,v 1.53 2021/09/26 21:29:38 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc.
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_init.c,v 1.52 2021/02/04 21:07:06 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_init.c,v 1.53 2021/09/26 21:29:38 thorpej Exp $");
 
 #include <sys/param.h>
 #include <sys/mount.h>
@@ -455,6 +455,9 @@ vfsinit(void)
 	 * included in the kernel.
 	 */
 	module_init_class(MODULE_CLASS_VFS);
+
+	extern kmutex_t fs_klist_lock;
+	mutex_init(&fs_klist_lock, MUTEX_DEFAULT, IPL_NONE);
 }
 
 /*

Index: src/sys/kern/vfs_syscalls.c
diff -u src/sys/kern/vfs_syscalls.c:1.552 src/sys/kern/vfs_syscalls.c:1.553
--- src/sys/kern/vfs_syscalls.c:1.552	Sat Sep 11 10:08:55 2021
+++ src/sys/kern/vfs_syscalls.c	Sun Sep 26 21:29:38 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_syscalls.c,v 1.552 2021/09/11 10:08:55 riastradh Exp $	*/
+/*	$NetBSD: vfs_syscalls.c,v 1.553 2021/09/26 21:29:38 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009, 2019, 2020 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.552 2021/09/11 10:08:55 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.553 2021/09/26 21:29:38 thorpej Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_fileassoc.h"
@@ -165,6 +165,63 @@ const char * const mountcompatnames[] = 
 
 const u_int nmountcompatnames = __arraycount(mountcompatnames);
 
+/*
+ * Filter event method for EVFILT_FS.
+ */
+static struct klist fs_klist = SLIST_HEAD_INITIALIZER(&fs_klist);
+kmutex_t fs_klist_lock;
+
+CTASSERT((NOTE_SUBMIT & VQ_MOUNT) == 0);
+CTASSERT((NOTE_SUBMIT & VQ_UNMOUNT) == 0);
+
+static int
+filt_fsattach(struct knote *kn)
+{
+	mutex_enter(&fs_klist_lock);
+	kn->kn_flags |= EV_CLEAR;
+	SLIST_INSERT_HEAD(&fs_klist, kn, kn_selnext);
+	mutex_exit(&fs_klist_lock);
+
+	return 0;
+}
+
+static void
+filt_fsdetach(struct knote *kn)
+{
+	mutex_enter(&fs_klist_lock);
+	SLIST_REMOVE(&fs_klist, kn, knote, kn_selnext);
+	mutex_exit(&fs_klist_lock);
+}
+
+static int
+filt_fs(struct knote *kn, long hint)
+{
+	int rv;
+
+	if (hint & NOTE_SUBMIT) {
+		KASSERT(mutex_owned(&fs_klist_lock));
+		kn->kn_fflags |= hint & ~NOTE_SUBMIT;
+	} else {
+		mutex_enter(&fs_klist_lock);
+	}
+
+	rv = (kn->kn_fflags != 0);
+
+	if ((hint & NOTE_SUBMIT) == 0) {
+		mutex_exit(&fs_klist_lock);
+	}
+
+	return rv;
+}
+
+/* referenced in kern_event.c */
+const struct filterops fs_filtops = {
+	.f_flags = FILTEROP_MPSAFE,
+	.f_attach = filt_fsattach,
+	.f_detach = filt_fsdetach,
+	.f_event = filt_fs,
+};
+
 static int 
 fd_nameiat(struct lwp *l, int fdat, struct nameidata *ndp)
 {
@@ -553,8 +610,11 @@ do_sys_mount(struct lwp *l, const char *
 		    &data_len);
 		vfsopsrele = false;
 	}
-	if (!error)
-		KNOTE(&fs_klist, VQ_MOUNT);
+	if (!error) {
+		mutex_enter(&fs_klist_lock);
+		KNOTE(&fs_klist, NOTE_SUBMIT | VQ_MOUNT);
+		mutex_exit(&fs_klist_lock);
+	}
 
     done:
 	if (vfsopsrele)
@@ -633,8 +693,11 @@ sys_unmount(struct lwp *l, const struct 
 	vrele(vp);
 	error = dounmount(mp, SCARG(uap, flags), l);
 	vfs_rele(mp);
-	if (!error)
-		KNOTE(&fs_klist, VQ_UNMOUNT);
+	if (!error) {
+		mutex_enter(&fs_klist_lock);
+		KNOTE(&fs_klist, NOTE_SUBMIT | VQ_UNMOUNT);
+		mutex_exit(&fs_klist_lock);
+	}
 	return error;
 }
 

Index: src/sys/sys/event.h
diff -u src/sys/sys/event.h:1.42 src/sys/sys/event.h:1.43
--- src/sys/sys/event.h:1.42	Sun Sep 26 03:12:50 2021
+++ src/sys/sys/event.h	Sun Sep 26 21:29:39 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: event.h,v 1.42 2021/09/26 03:12:50 thorpej Exp $	*/
+/*	$NetBSD: event.h,v 1.43 2021/09/26 21:29:39 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jle...@freebsd.org>
@@ -271,7 +271,7 @@ struct knote {
 #define	kn_data		kn_kevent.data
 };
 
-#include <sys/systm.h> /* for copyin_t */
+#include <sys/systm.h>	/* for copyin_t */
 
 struct lwp;
 struct timespec;
@@ -307,8 +307,6 @@ int	kfilter_unregister(const char *);
 int	filt_seltrue(struct knote *, long);
 extern const struct filterops seltrue_filtops;
 
-extern struct klist fs_klist;	/* EVFILT_FS */
-
 #else 	/* !_KERNEL */
 
 #include <sys/cdefs.h>

Reply via email to