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 <[email protected]>
Reviewed by:	kib
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D60364

(cherry picked from commit c2ac0db715)
This commit is contained in:
Mark Johnston committed 2026-10-22 15:45:41 +00:00
1 parent b608a3eb35
commit c781d6774d
2 files changed
+53

No files matched your search

+18
View File
@@ -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,
+35
View File
@@ -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);