git: c2ac0db7150c - main - devfs: Explicitly disable inotify

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 07 Oct 2026 14:56:08 UTC
The branch main has been updated by markj:

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

commit c2ac0db7150c53d3902dbe1c507d96cbacfcadc6
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-10-07 14:36:26 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-07 14:55:57 +0000

    devfs: Explicitly disable inotify
    
    In principle, inotify can be used to leak timing information about I/O
    device accesses by privileged process, which might be sensitive, e.g.,
    in the case of /dev/input.  See https://inoti.fyi/#1-linux-inter-keystroke-timing
    
    In practice this does not work since I/O to devfs nodes does not go
    through vnodes, so the INOTIFY() calls in various VOP hooks do not fire.
    But, let's explicitly define a no-op VOP_INOTIFY to help ensure that no
    one tries to "fix" this bug.
    
    Alternately we could simply ignore VCHR/VBLK vnodes in vn_inotify().
    However, maybe someday it'll be desirable to enable inotify for specific
    device files, in which case the filtering should live in devfs.
    
    Add a regression test.
    
    Reported by:    Sudheendra Raghav Neela <sneela@tugraz.at>
    Reviewed by:    kib
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D60364
---
 sys/fs/devfs/devfs_vnops.c    | 18 ++++++++++++++++++
 tests/sys/kern/inotify_test.c | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 53fb08c6931b..9ed7ef9f0aed 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -921,6 +921,22 @@ fiodgname_buf_get_ptr(void *fgnp, u_long com)
 	}
 }
 
+static int
+devfs_inotify(struct vop_inotify_args *ap)
+{
+	/*
+	 * Don't provide inotify for devfs nodes.  Most of the inotify hooks
+	 * don't fire anyway since devfs files do not use the vnode I/O path.
+	 * If that were not the case, however, inotify could be abused to leak
+	 * sensitive timing information, e.g., by watching for reads from
+	 * /dev/input.  Thus, we make inotify do nothing here as a safeguard.
+	 *
+	 * At some point it may be desirable to enable inotify for specific
+	 * device files.
+	 */
+	return (0);
+}
+
 static int
 devfs_ioctl(struct vop_ioctl_args *ap)
 {
@@ -2126,6 +2142,7 @@ static struct vop_vector devfs_vnodeops = {
 
 	.vop_access =		devfs_access,
 	.vop_getattr =		devfs_getattr,
+	.vop_inotify =		devfs_inotify,
 	.vop_ioctl =		devfs_rioctl,
 	.vop_lookup =		devfs_lookup,
 	.vop_mknod =		devfs_mknod,
@@ -2159,6 +2176,7 @@ static struct vop_vector devfs_specops = {
 	.vop_create =		VOP_PANIC,
 	.vop_fsync =		vop_stdfsync,
 	.vop_getattr =		devfs_getattr,
+	.vop_inotify =		devfs_inotify,
 	.vop_ioctl =		devfs_ioctl,
 	.vop_link =		VOP_PANIC,
 	.vop_mkdir =		VOP_PANIC,
diff --git a/tests/sys/kern/inotify_test.c b/tests/sys/kern/inotify_test.c
index 0c2cc260f29e..8f16b9949932 100644
--- a/tests/sys/kern/inotify_test.c
+++ b/tests/sys/kern/inotify_test.c
@@ -318,6 +318,40 @@ ATF_TC_BODY(inotify_mask_create, tc)
 	close_inotify(ifd);
 }
 
+/*
+ * Verify that inotify events are not generated for devfs.  In principle such
+ * events could leak timing information about I/O device accesses.
+ */
+ATF_TC_WITHOUT_HEAD(inotify_devfs);
+ATF_TC_BODY(inotify_devfs, tc)
+{
+	char buf[1];
+	int fd, ifd, n, wd1, wd2, wd3;
+
+	ifd = inotify(IN_NONBLOCK);
+
+	wd1 = inotify_add_watch(ifd, "/dev", IN_ALL_EVENTS);
+	ATF_REQUIRE(wd1 >= 0);
+	wd2 = inotify_add_watch(ifd, "/dev/null", IN_ALL_EVENTS);
+	ATF_REQUIRE(wd2 >= 0);
+	wd3 = inotify_add_watch(ifd, "/dev/zero", IN_ALL_EVENTS);
+	ATF_REQUIRE(wd3 >= 0);
+
+	fd = open("/dev/null", O_WRONLY);
+	ATF_REQUIRE(fd != -1);
+	ATF_REQUIRE(write(fd, buf, sizeof(buf)) == (ssize_t)sizeof(buf));
+	close_checked(fd);
+
+	fd = open("/dev/zero", O_RDONLY);
+	ATF_REQUIRE(fd != -1);
+	ATF_REQUIRE(read(fd, buf, sizeof(buf)) == (ssize_t)sizeof(buf));
+	close_checked(fd);
+
+	ATF_REQUIRE(ioctl(ifd, FIONREAD, &n) == 0);
+	ATF_REQUIRE_MSG(n == 0, "unexpected %d bytes of inotify events", n);
+	close_checked(ifd);
+}
+
 /*
  * Make sure that inotify cooperates with nullfs: if a lower vnode is the
  * subject of an event, the upper vnode should be notified, and if the upper
@@ -1011,6 +1045,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, inotify_capsicum);
 	ATF_TP_ADD_TC(tp, inotify_coalesce);
 	ATF_TP_ADD_TC(tp, inotify_mask_create);
+	ATF_TP_ADD_TC(tp, inotify_devfs);
 	ATF_TP_ADD_TC(tp, inotify_nullfs);
 	ATF_TP_ADD_TC(tp, inotify_nullfs_remove);
 	ATF_TP_ADD_TC(tp, inotify_nullfs_rename);