NetBSD Problem Report #60756
From www@netbsd.org Sun Sep 20 13:21:35 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits)
client-signature RSA-PSS (2048 bits))
(Client CN "mail.netbsd.org", Issuer "YR1" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id C111B1A923B
for <gnats-bugs@gnats.NetBSD.org>; Sun, 20 Sep 2026 13:21:35 +0000 (UTC)
Message-Id: <20260920132134.967D91A923C@mollari.NetBSD.org>
Date: Sun, 20 Sep 2026 13:21:34 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: compat_linux inotify(2): TOCTOU and locking shenanigans
X-Send-Pr-Version: www-1.0
X-From4GNATS: "campbell+netbsd@mumble.net via gnats" <gnats-admin@NetBSD.org>
>Number: 60756
>Category: kern
>Synopsis: compat_linux inotify(2): TOCTOU and locking shenanigans
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: needs-pullups
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Sep 20 13:25:01 +0000 2026
>Closed-Date:
>Last-Modified: Fri Oct 02 03:30:36 +0000 2026
>Originator: Taylor R Campbell
>Release: current, 11, 10, ...
>Organization:
The CompatBSD Inotification, Inc.
>Environment:
>Description:
inotify_readdir sometimes runs with the vnode lock, and
sometimes unlocks and relocks the interlock, which strikes
me as incoherent and likely fundamentally buggy:
790 /* XXX: should pass whether to lock or not */
791 if (needs_lock)
792 vn_lock(vp, LK_SHARED | LK_RETRY);
793 else
794 /*
795 * XXX We need to temporarily drop v_interlock because
796 * it may be temporarily acquired by biowait().
797 */
798 mutex_exit(vp->v_interlock);
799 KASSERT(!mutex_owned(vp->v_interlock));
800 error = VOP_READDIR(vp, &uio, fp->f_cred, &eofflag, NULL, NULL);
801 if (needs_lock)
802 VOP_UNLOCK(vp);
803 else
804 mutex_enter(vp->v_interlock);
https://nxr.netbsd.org/xref/src/sys/compat/linux/common/linux_inotify.c?r=1.8#761
It is not clear to me whether in the needs_lock=false case it
actually holds the vnode lock. This should be asserted.
The needs_lock=true case used by get_inotify_dir_entries
creates a TOCTOU issue: in one pass, it it counts the number of
entries with inotify_readdir; in a second pass, it copies them
into an array of that many entries -- but the number of entries
may have changed between the first pass and the second pass
(with some large entries being split into multiple small ones),
possibly leading the inner while loop in the second pass to
walk off the end of the array.
842 mutex_enter(&wp->f_lock);
843 wp->f_offset = 0;
844 mutex_exit(&wp->f_lock);
845 decount = 0;
846 for (;;) {
847 error = inotify_readdir(wp, &de, &done, needs_lock);
848 if (error != 0)
849 goto leave;
850 if (done == 0)
851 break;
852
853 currdep = &de;
854 while ((char *)currdep < ((char *)&de) + done) {
855 decount++;
856 currdep = _DIRENT_NEXT(currdep);
857 }
858 }
859
860 idep = kmem_zalloc(INOTIFY_DIR_ENTRIES_SIZE(decount), KM_SLEEP);
861 idep->ide_count = decount;
862
863 mutex_enter(&wp->f_lock);
864 wp->f_offset = 0;
865 mutex_exit(&wp->f_lock);
866 for (i = 0; i < decount;) {
867 error = inotify_readdir(wp, &de, &done, needs_lock);
868 if (error != 0 || done == 0) {
869 kmem_free(idep, INOTIFY_DIR_ENTRIES_SIZE(decount));
870 idep = NULL;
871 goto leave;
872 }
873
874 currdep = &de;
875 while ((char *)currdep < ((char *)&de) + done) {
876 idep->ide_entries[i].fileno = currdep->d_fileno;
877 strcpy(idep->ide_entries[i].name, currdep->d_name);
878
879 currdep = _DIRENT_NEXT(currdep);
880 i++;
881 }
882 }
https://nxr.netbsd.org/xref/src/sys/compat/linux/common/linux_inotify.c?r=1.8#842
>How-To-Repeat:
code inspection
>Fix:
1. At least bound the inner while loop in the second pass of
get_inotify_dir_entries.
2. Fix the locking shenanigans.
3. Nix strcpy while here; use strlcpy, strncpy, or copystr
instead, depending on the nature of the input and output
buffers.
>Release-Note:
>Audit-Trail:
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60756 CVS commit: src/sys/compat/linux/common
Date: Sun, 20 Sep 2026 13:47:39 +0000
Module Name: src
Committed By: riastradh
Date: Sun Sep 20 13:47:39 UTC 2026
Modified Files:
src/sys/compat/linux/common: linux_inotify.c
Log Message:
compat_linux inotify(2): strcpy -> strlcpy
PR kern/60756: compat_linux inotify(2): TOCTOU and locking shenanigans
To generate a diff of this commit:
cvs rdiff -u -r1.9 -r1.10 src/sys/compat/linux/common/linux_inotify.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60756 CVS commit: src/sys/compat/linux/common
Date: Sun, 20 Sep 2026 13:48:00 +0000
Module Name: src
Committed By: riastradh
Date: Sun Sep 20 13:48:00 UTC 2026
Modified Files:
src/sys/compat/linux/common: linux_inotify.c
Log Message:
compat_linux inotify(2): Re-check number of entries in loop.
Might have changed between two calls to inotify_readdir.
XXX Should maybe not do two calls to inotify_readdir! Can we hold
the vnode lock or use a fixed temporary buffer size or something?
PR kern/60756: compat_linux inotify(2): TOCTOU and locking shenanigans
To generate a diff of this commit:
cvs rdiff -u -r1.10 -r1.11 src/sys/compat/linux/common/linux_inotify.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
State-Changed-From-To: open->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Fri, 02 Oct 2026 03:30:36 +0000
State-Changed-Why:
partly fixed in HEAD, needs pullup-11 and pullup-10
...then back to open to fix the lock shenanigans and underlying TOCTOU
issue
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.