UFS inode-hash has no lock; concurrent ffs_vget() inserts orphan an inode and panic on reclaim (dual-vnode on production)
| Field | Value |
|---|---|
| ID | DF-0928 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H |
| CWE | CWE-362 Concurrent Execution using Shared Resource with Improper Synchronization |
| File | sys/vfs/ufs/ufs_ihash.c |
| Lines | 78-189 |
| Area | vfs |
| Confidence | likely |
| Discovered | 2026-07-05 |
| Reported | pending |
| Known CVE | none |
| CVE match | dfly_specific |
Summary
Every walk/insert/remove of the per-mount UFS inode hash chain in
sys/vfs/ufs/ufs_ihash.c runs with no synchronization primitive
whatsoever β there is no mutex, no lwkt_token, no vnode-held
requirement on entry. The otherwise-identical sibling
sys/vfs/ext2fs/ext2_ihash.c wraps each of these operations in a static
lwkt_token (ext2_ihash.c:53,94,131,156) and explicitly documents that
it exists to serialize the chain. Because ufs_ihashins's
duplicate-detection scan (ufs_ihash.c:156-161) and the actual
*ipp = ip store (ufs_ihash.c:163) are not atomic w.r.t. each other,
two concurrent ffs_vget() calls for the same (dev,ino) can both
observe an empty bucket and both write the bucket head, orphaning the
first writer's inode (IN_HASHED set but unreachable). On INVARIANTS
kernels the orphan trips KKASSERT(ip == iq) at ufs_ihash.c:184
during ufs_reclaim β panic; on production kernels it produces two live
vnodes for one on-disk inode with divergent cached metadata (cache
incoherence, stale mode/permissions).
Root cause
No synchronization protects ump->um_ihashtbl or the inode.i_next
chain.
(a) ufs_ihashins (ufs_ihash.c:148-166): the duplicate scan
while ((iq = *ipp) != NULL) {
if (ip->i_dev == iq->i_dev && ip->i_number == iq->i_number) {
return(EBUSY);
}
ipp = &iq->i_next;
}
ip->i_next = NULL;
*ipp = ip; /* <-- racing store */
ip->i_flag |= IN_HASHED;
is a classic check-then-act TOCTOU. Two threads A and B in ffs_vget
(ffs_vfsops.c:1066) for the same ino both miss in ufs_ihashget
(ffs_vfsops.c:1081), both getnewvnode + kmalloc an inode, and both
call ufs_ihashins. If both read *ipp == NULL for the bucket before
either executes *ipp = ip, the second store silently overwrites the
first; thread A's inode has i_flag |= IN_HASHED (ufs_ihash.c:164)
but is not reachable through the chain.
The EBUSY retry in ffs_vfsops.c:1120-1128 only catches the
detected case (B sees A's entry); it does nothing for the undetected
dual-store.
(b) ufs_ihashrem (ufs_ihash.c:171-189): later walks the chain
for thread A's orphan, fails to find it, and fires
KKASSERT(ip == iq) at ufs_ihash.c:184 (iq is NULL).
(c) Walkers (ufs_ihashget, ufs_ihashlookup, ufs_ihashcheck):
all dereference ip->i_number, ip->i_dev, and ip->i_next from the
chain with no lock and no reference on the inode; a concurrent
ufs_reclaim (ufs_inode.c:108-165) which runs ufs_ihashrem
(ufs_inode.c:145) and then kfree(ip) (ufs_inode.c:162) leaves a
narrow use-after-free-read window on the walked inode.
Contrast with the correct sibling implementation
sys/vfs/ext2fs/ext2_ihash.c which declares
static struct lwkt_token ext2_ihash_token (ext2_ihash.c:53) and
acquires it in ext2_ihashget (:94,114,117), ext2_ihashins
(:131,135,143), and ext2_ihashrem (:156,169) β ufs_ihash.c is
missing every one of those calls.
Threat model & preconditions
- Attacker position: Local, unprivileged.
- Privileges gained or impact:
1. INVARIANTS/Debug kernel: reliable local DoS via
KKASSERTpanic inufs_ihashremwhen the orphan is reclaimed. 2. Production (non-INVARIANTS) kernel: cache incoherence β two independentstruct inode/vnode copies of the same on-disk inode. Metadata changes (chmod/chown/setuid, link count, size) made through one vnode are invisible to lookups going through the hash β stale permission checks / setuid-bit retention / divergentffs_updatewrites (I:L). 3. NFS-exported UFS: remote variant.nfs_serv.cbuildsREADDIRPLUSreplies by callingVFS_VGETonce per directory entry (nfs_serv.c:3417,3454), so two concurrent NFS clients (or one multi-threaded client) issuingREADDIRPLUS/LOOKUPfor overlapping entries drive concurrentffs_vgetβ concurrentufs_ihashinsfor the samedp->d_ino. An attacker controlling an NFS client does not need credentials beyond what the export already grants. - Required config or capabilities: A UFS (FFS/MFS) filesystem; for
the remote vector,
nfsdrunning and the filesystem exported. - Reachability: Spawn N threads that, after a barrier, all
open()the same file whose vnode has just been evicted from the cache (eviction forced by thrashing the vnode cache, or simply racing the very first lookup of a freshly-created file). All N threads miss inufs_ihashgetand race intoufs_ihashins.
Proof of concept
PoC source: findings/poc/DF-0928/race_ufs_ihash.c
Build & run
cc -O2 -pthread -o race_ufs_ihash race_ufs_ihash.c ./race_ufs_ihash # on a UFS-mounted /tmp
Expected output
On an INVARIANTS kernel:
Kernel panic: ufs_ihashrem ... ufs_ihashrem(...) at ufs_ihashrem+0x... (ufs_ihash.c:184 KKASSERT) ufs_reclaim(...) at ufs_reclaim+0x... vclean(...) at vclean+0x... ...
On a production kernel, observe two vnodes for the same (dev, ino) via
fstat/vfsaudit, or run a side-channel check that
chmod(04755)-then-chmod(0644) on the racing threads leaves a vnode
still reporting the setuid bit.
For the remote variant: export /srv via NFS, then from two client
hosts run find /mnt -exec true {} + (READDIRPLUS-heavy) in a tight
loop; the server eventually panics the same way.
Impact
- INVARIANTS kernel: reliable local DoS (panic) β and a remote DoS on NFS-exported UFS.
- Production kernel: cache-incoherence β stale permission/setuid checks on a file that an attacker can race on; integrity violation.
Recommended fix
Add a per-mount lwkt_token mirroring the proven ext2_ihash.c
pattern, and acquire it around every chain walk/insert/remove. The
token is held across the blocking vget() in ufs_ihashget exactly as
ext2_ihashget holds it across its vget β this is the established
DragonFly idiom and makes the check-then-act in ufs_ihashins atomic
w.r.t. other inserters/removers.
--- a/sys/vfs/ufs/ufsmount.h
+++ b/sys/vfs/ufs/ufsmount.h
@@ -84,6 +84,8 @@ struct ufsmount {
struct malloc_type *um_malloctype; /* The inodes malloctype */
int um_i_effnlink_valid; /* i_effnlink valid? */
struct inode **um_ihashtbl; /* inum to inode map */
u_long um_ihash; /* size of hash table - 1 */
+ struct lwkt_token um_ihash_token; /* guards um_ihashtbl + i_next */
};
--- a/sys/vfs/ufs/ufs_ihash.c
+++ b/sys/vfs/ufs/ufs_ihash.c
@@ -33,6 +33,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/thread.h>
#include <sys/lock.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
@@ -55,12 +56,17 @@ static MALLOC_DEFINE(M_UFSIHASH, "UFS ihash", "UFS Inode hash tables");
void
ufs_ihashinit(struct ufsmount *ump)
{
+ lwkt_token_init(&ump->um_ihash_token, "ufsihash");
+ lwkt_gettoken(&ump->um_ihash_token);
ump->um_ihash = vfs_inodehashsize();
ump->um_ihashtbl = kmalloc(sizeof(void *) * ump->um_ihash,
M_UFSIHASH,
M_WAITOK|M_ZERO);
--ump->um_ihash;
+ lwkt_reltoken(&ump->um_ihash_token);
}
void
ufs_ihashuninit(struct ufsmount *ump)
{
+ lwkt_gettoken(&ump->um_ihash_token);
if (ump->um_ihashtbl) {
kfree(ump->um_ihashtbl, M_UFSIHASH);
ump->um_ihashtbl = NULL;
}
+ lwkt_reltoken(&ump->um_ihash_token);
}
@@ -148,6 +154,8 @@ int
ufs_ihashins(struct ufsmount *ump, struct inode *ip)
{
struct inode **ipp;
struct inode *iq;
+ lwkt_gettoken(&ump->um_ihash_token);
KKASSERT((ip->i_flag & IN_HASHED) == 0);
ipp = INOHASH(ump, ip->i_number);
while ((iq = *ipp) != NULL) {
if (ip->i_dev == iq->i_dev && ip->i_number == iq->i_number) {
+ lwkt_reltoken(&ump->um_ihash_token);
return(EBUSY);
}
ipp = &iq->i_next;
}
ip->i_next = NULL;
*ipp = ip;
ip->i_flag |= IN_HASHED;
+ lwkt_reltoken(&ump->um_ihash_token);
return(0);
}
@@ -171,12 +179,15 @@ void
ufs_ihashrem(struct ufsmount *ump, struct inode *ip)
{
struct inode **ipp;
struct inode *iq;
+ lwkt_gettoken(&ump->um_ihash_token);
if (ip->i_flag & IN_HASHED) {
ipp = INOHASH(ump, ip->i_number);
while ((iq = *ipp) != NULL) {
if (ip == iq)
break;
ipp = &iq->i_next;
}
KKASSERT(ip == iq);
*ipp = ip->i_next;
ip->i_next = NULL;
ip->i_flag &= ~IN_HASHED;
}
+ lwkt_reltoken(&ump->um_ihash_token);
}
@@ ufs_ihashget / ufs_ihashlookup / ufs_ihashcheck: take the token at entry,
hold it across vget (in ufs_ihashget),
release on every return path.
Because lwkt_token is held by the calling thread and the walker
re-validates the chain after the blocking vget() (the existing
re-walk at ufs_ihash.c:115-122), concurrent inserters/removers are now
serialized: the second ufs_ihashins will observe the first's
*ipp = ip store and correctly return EBUSY, the existing ffs_vget
retry path handles it, and ufs_ihashrem's KKASSERT will no longer
fire on a legitimately-orphaned inode because no orphan can be created.
The narrower walk-UAF against concurrent ufs_reclaim is also closed
because the token serializes ufs_ihashrem (which runs under
ufs_reclaim) against every walker.
References
sys/vfs/ext2fs/ext2_ihash.c:53,94,131,156β the proven sibling implementation with the missing token.sys/vfs/ufs/ffs_vfsops.c:1066-1128βffs_vgetretry path that only catches the detected-duplicate case.sys/vfs/ufs/ufs_inode.c:108-165βufs_reclaimthat frees the inode afterufs_ihashrem.sys/vfs/nfs/nfs_serv.c:3417,3454βREADDIRPLUSdriving concurrentVFS_VGETcalls.
Timeline
- 2026-07-05 Discovered during automated audit.
- pending Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0928 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| race_ufs_ihash.c | trigger-source | original reviewer-written PoC (targets /tmp, fixed to use UFS mount) | 6.0 KB | view raw |
| race_ufs_ihash2.c | trigger-source | improved PoC: hardlinks in separate dirs + churner threads | 4.3 KB | view raw |
| race_ufs_ihash3.c | trigger-source | max-aggression PoC: 16 racers + 8 churners + 500 files on tiny-hash UFS | 4.2 KB | view raw |
| build.sh | build-script | cc -O2 -pthread -o race_ufs_ihash3 race_ufs_ihash3.c | 257 B | view raw |
| run.sh | run-script | run race_ufs_ihash3 on /mnt/ufs_test | 318 B | view raw |
| VERDICT.md | verdict | full narrative: source-level confirmation, namecache serialization analysis, fix validation | 6.6 KB | β raw |
| fix.diff | suggested-fix | git-apply-able: adds lwkt_token to all 7 ufs_ihash functions, mirroring ext2_ihash.c | 3.0 KB | view raw |
| fix_build.log | build-log | full nativekernel build output for fixed kernel (rc=0) | 5.6 MB | β download |
| fix_run.log | run-log | PoC run on fixed kernel (no panic) | 142 B | view raw |
| README.md | readme | human reproduce doc | 1.6 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0928 β PoC: UFS inode-hash dual-insert race
Goal
Force a dual ufs_ihashins store for the same (dev,ino) by racing
N threads' open() calls on a file whose vnode has just been evicted
from the cache. The loser inode is orphaned (IN_HASHED set but not on
the chain). On INVARIANTS kernels the orphan trips
KKASSERT(ip == iq) at ufs_ihash.c:184 when reclaimed; on production
kernels it produces two vnodes for one inode with divergent cached
metadata.
Build & run (DragonFlyBSD guest with UFS-mounted /tmp)
cc -O2 -pthread -o race_ufs_ihash race_ufs_ihash.c ./race_ufs_ihash
Optional (root): sysctl vfs.maxvnodes=256 to make reproduction
near-deterministic.
Expected output
INVARIANTS kernel
Kernel panic: ufs_ihashrem ... ufs_ihashrem(...) at ufs_ihashrem+0x... (ufs_ihash.c:184 KKASSERT) ufs_reclaim(...) at ufs_reclaim+0x... vclean(...) at vclean+0x... vrecycle(...) at vrecycle+0x... ...
Production (non-INVARIANTS) kernel
No panic; instead observe two vnodes for the same (dev, ino) via
fstat | awk '$2 == <ino>', or run a side-channel check that
chmod(04755)-then-chmod(0644) from racing threads leaves a vnode
still reporting the setuid bit.
Notes
- The remote variant: export
/srvvia NFS, then from two client hosts runfind /mnt -exec true {} +(READDIRPLUS-heavy) in a tight loop; the server eventually panics the same way. - This is the same concurrency bug class as the FFS-era inode-hash races
fixed in FreeBSD/NetBSD years ago; DragonFlyBSD's
ext2_ihash.chas the fix (alwkt_token),ufs_ihash.cdoes not.
DF-0928 β VERDICT
Verdict: REPRODUCED AT SOURCE LEVEL β missing lock confirmed; race not triggered from local userspace
The bug described in DF-0928 is real and confirmed at the source level: the
UFS/FFS inode hash (sys/vfs/ufs/ufs_ihash.c) has zero synchronization
protecting the hash table or the inode.i_next chain. Every function β
ufs_ihashinit, ufs_ihashuninit, ufs_ihashget, ufs_ihashlookup,
ufs_ihashcheck, ufs_ihashins, ufs_ihashrem β walks or modifies the chain
with no mutex, no lwkt_token, no held reference. The otherwise-identical
sibling sys/vfs/ext2fs/ext2_ihash.c wraps every one of these operations in a
static struct lwkt_token ext2_ihash_token (ext2_ihash.c:53).
However, despite extensive testing (multiple PoC variants, 5+ minutes of
aggressive racing, a delay-injected kernel that widened the TOCTOU window to
5ms inside ufs_ihashins), no panic was triggered from local userspace.
The root cause is that the DragonFlyBSD namecache provides incidental
serialization that prevents concurrent ufs_iget/ufs_ihashins calls for the
same inode from local processes.
Mechanism (confirmed by source analysis)
The missing lock (FACT)
ufs_ihash.c (entire 190-line file read) contains not a single
synchronization primitive. Compare:
| Function | ufs_ihash.c |
ext2_ihash.c (sibling) |
|---|---|---|
*_init |
no lock | lwkt_token_init (:67) |
*_uninit |
no lock | lwkt_gettoken/reltoken (:73,76) |
*_get |
no lock | lwkt_gettoken/reltoken (:94,114,117) |
*_ins |
no lock | lwkt_gettoken/reltoken (:131,135,143) |
*_rem |
no lock | lwkt_gettoken/reltoken (:156,169) |
The TOCTOU in ufs_ihashins (ufs_ihash.c:154-164)
// SCAN: check for existing entry (no lock held)
while ((iq = *ipp) != NULL) {
if (ip->i_dev == iq->i_dev && ip->i_number == iq->i_number)
return(EBUSY);
ipp = &iq->i_next;
}
// *** RACE WINDOW: another thread can scan+store here ***
ip->i_next = NULL;
*ipp = ip; // STORE: overwrites any concurrent store
ip->i_flag |= IN_HASHED;
Two concurrent ffs_vget() calls for the same (dev,ino) can both scan the
empty bucket, both see no duplicate, and both store β the second store silently
overwrites the first, orphaning the loser's inode (IN_HASHED set but not on
the chain). On INVARIANTS kernels, the orphan trips KKASSERT(ip == iq) at
ufs_ihash.c:184 during ufs_reclaim β ufs_ihashrem.
The walker-UAF (ufs_ihash.c:105, 138, 179)
All walker functions dereference ip->i_number, ip->i_dev, ip->i_next
from the chain with no lock and no reference on the walked inode. A concurrent
ufs_reclaim (ufs_inode.c:145 β ufs_ihashrem) β kfree(ip)
(ufs_inode.c:162) leaves a use-after-free-read window.
Why the race was NOT triggered from local userspace
Instrumented-kernel testing (kprintf + 5ms DELAY inside the TOCTOU window of
ufs_ihashins) confirmed:
ufs_ihashinsIS called β 1139 calls observed during an 8-second run, from multiple CPUs (cpu=0, cpu=1, cpu=5).ihashins collision(EBUSY) count = 0 β no two threads ever calledufs_ihashinsfor the same inode concurrently.- Root cause: the DragonFlyBSD namecache provides incidental serialization.
When any thread resolves ANY path to an inode and calls
ufs_ihashins, the vnode is created and inserted into the hash. All subsequent lookups via ANY path (including different hardlink paths in separate directories) callufs_ihashgetβ hit β return the existing vnode. No secondufs_ihashinscall occurs for the same inode.
The dual-insert race window requires the inode's vnode to have been fully
reclaimed (removed from hash by ufs_ihashrem) AND two lookups to both miss
ufs_ihashget before either's ufs_ihashins stores. The namecache resolves
the first lookup within microseconds, closing the window before a second
lookup can enter.
The realistic trigger: NFS READDIRPLUS
The finding's NFS variant (nfs_serv.c builds READDIRPLUS replies by calling
VFS_VGET once per directory entry, nfs_serv.c:3417,3454) bypasses the
namecache and drives concurrent ffs_vget for overlapping entries directly.
Two NFS clients issuing concurrent READDIRPLUS for the same directory would
race. This was not tested (no NFS server setup on this guest).
Exploit chain assessment
This is a race condition (CWE-362), not a deterministic memory-corruption
primitive. There is no reliable write/control primitive to escalate. On an
INVARIANTS kernel (default GENERIC), the impact is DoS (panic). On a production
(non-INVARIANTS) kernel, the impact would be cache incoherence (two vnodes for
one inode β stale permissions). No uid=0 escalation path was identified.
Fix
Authored fix.diff: adds a per-mount struct lwkt_token um_ihash_token to
struct ufsmount (ufsmount.h) and acquires/releases it in all 7 functions
(ufs_ihashinit, ufs_ihashuninit, ufs_ihashget, ufs_ihashlookup,
ufs_ihashcheck, ufs_ihashins, ufs_ihashrem). This exactly mirrors the
proven ext2_ihash.c pattern.
Fix validation
- Applies:
git apply --checkpasses cleanly (133-line diff, 2 files). - Compiles:
make -j6 nativekernelrc=0 (full build log infix_build.log). - Boots: fixed kernel
#1boots and runs normally. - PoC on fixed kernel: runs 50s without panic (same as unpatched β the race can't be triggered from local userspace on either kernel due to namecache serialization).
- Behavioral before/after:
not_testableβ the PoC cannot trigger the race on either kernel. The fix is validated at the code level: all 7 hash operations are now serialized by the token, matching the proven ext2fs sibling.
PoC files
race_ufs_ihash.cβ original PoC (reviewer-written, operates on/tmpwhich is tmpfs on this guest β corrected to use a UFS mount).race_ufs_ihash2.cβ improved PoC using hardlinks in separate directories.race_ufs_ihash3.cβ maximum-aggression PoC: 16 racers + 8 churners + 500 pre-created files on a tiny-hash UFS mount.
PoC changes
- Changed target from
/tmp(tmpfs on this guest β does NOT exercise UFS inode hash) to a UFS mount (/mnt/ufs_test, created viavnconfig+newfs). - Added hardlink-based racing across separate directories to bypass the parent-directory vnode lock that serializes same-directory lookups.
- Added churner threads (create+delete files) to maintain vnode recycling pressure.
- Created two additional PoC variants (
race_ufs_ihash2.c,race_ufs_ihash3.c) with progressively more aggressive racing strategies.
Fix verification
not_testableNOT TESTABLE behaviorally: the PoC cannot trigger the race on either the unpatched baseline or the single-fix kernel because the namecache provides incidental serialization. The fix was validated at the code level: git apply --check passes, make -j6 nativekernel rc=0, the fixed kernel #1 boots and runs the PoC without issue, all 7 hash operations are now serialized by lwkt_token matching ext2_ihash.c.
Baseline (#0): PoC ran 150s on UFS mount, no panic, guest up. Delay-injected (#1, 5ms DELAY): 50s, no panic, 1139 ihashins calls, 0 EBUSY collisions. Fixed kernel (#1, lwkt_token): 50s, no panic, guest up. All three kernels exhibit identical behavior because the namecache prevents the race from local userspace.
Confirmed kernel references
Detail
Exploit chain
Race condition (CWE-362), not a deterministic memory-corruption primitive. The dual-insert TOCTOU could theoretically orphan an inode. The walker-UAF could theoretically read poisoned memory. Neither was triggered from local userspace because the namecache serializes concurrent VFS_VGET calls for the same inode. No uid0 escalation path exists -- this is a DoS/cache-incoherence bug. The finding's threat model (NFS READDIRPLUS) is the realistic trigger but was not testable on this guest.
Evidence (decisive lines)
Source-level (definitive): ufs_ihash.c has 0 lock acquisitions in all 190 lines; ext2_ihash.c has 11 lwkt_gettoken/reltoken calls. Instrumented kernel: 1139 ufs_ihashins calls from cpu=0,1,5 but 'ihashins collision' (EBUSY) count=0 -- no concurrent same-inode inserts. Delay-injected kernel (5ms DELAY inside TOCTOU): also no panic after 50s of aggressive racing.
PoC changes
Fixed target FS (original PoC used /tmp which is tmpfs); added UFS mount setup via vnconfig+newfs. Created 3 PoC variants: hardlinks across separate dirs, 16 racers + 8 churners + 500 pre-created files, delay-injected kernel. Authored fix.diff (lwkt_token in all 7 ufs_ihash functions, matching ext2_ihash.c).
Verified recommended fix
Add a per-mount struct lwkt_token um_ihash_token to struct ufsmount (ufsmount.h:88) and acquire/release it in all 7 ufs_ihash functions, exactly mirroring the proven ext2_ihash.c pattern. The token is held across the blocking vget() in ufs_ihashget (as ext2_ihashget does at ext2_ihash.c:94,114). This closes the TOCTOU in ufs_ihashins and serializes walkers against ufs_ihashrem. Matches finding proposal; extends coverage to ufs_ihashlookup and ufs_ihashcheck. Full git-apply-able diff in findings/poc/DF-0928/fix.diff.
Verdict
NOT REPRODUCED at runtime, but the code-level defect is CONFIRMED REAL. The UFS inode hash (sys/vfs/ufs/ufs_ihash.c, entire 190-line file) has ZERO synchronization -- no mutex, no lwkt_token, no held-reference requirement on any of its 7 functions. The sibling ext2_ihash.c correctly wraps every operation in lwkt_token ext2_ihash_token. The TOCTOU in ufs_ihashins (scan at :156-161, store at :163, no atomicity) and the KKASSERT(ip==iq) at ufs_ihash.c:184 are both real. However, despite extensive testing (3 PoC variants, 5+ minutes of aggressive racing, delay-injected kernel widening TOCTOU to 5ms), NO PANIC was triggered. ROOT CAUSE: the DragonFlyBSD namecache provides incidental serialization -- once any thread resolves ANY path to an inode, the vnode is in the hash, and ALL subsequent lookups hit ufs_ihashget. The realistic trigger is NFS READDIRPLUS which bypasses the namecache -- not testable on this guest.
No comments yet.