vtruncbuf() caches the separately-allocated nc_name pointer across unlocked blocking work, then prints it (narrow UAF read)
Summary
vtruncbuf snapshots TAILQ_FIRST(&vp->v_namecache)->nc_name under vp->v_spin, drops the spin, blocks in the re-clean RB_SCAN loop, then kprintf()s the pointer. nc_name is a separately allocated string; a rename/unlink racing the truncation's rarely-taken re-clean path can cache_zap the ncp and free the name in the window β UAF read of freed heap into dmesg. Not PoC'd (contrived re-clean race, Low); strlcpy-snapshot-under-spin fix compiled as part of the guest kernel build used for DF-5807 fix validation.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5810 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.2 KB | β raw | |
| VERDICT.md | β | 1.1 KB | β raw | |
| fix.diff | β | 828 B | view raw | |
| verdict.json | β | 1.7 KB | view raw |
DF-5810 β vtruncbuf() caches nc_name across unlocked blocking work (narrow UAF read)
What
vtruncbuf() (sys/kern/vfs_subr.c:531-534) snapshots
TAILQ_FIRST(&vp->v_namecache)->nc_name under vp->v_spin, then
DROPS the spin and runs the re-clean RB_SCAN loop β which sleeps in
BUF_LOCK/bwrite β before kprintf(... %s, filename) dereferences the
pointer at vfs_subr.c:551. nc_name is a separately allocated
string (sys/sys/namecache.h:136); the namecache entry can be
cache_zap()d (rename/unlink race on the file being truncated) during
the blocking window, leaving filename dangling β use-after-free
read of freed heap into the console/dmesg output.
Preconditions: the rarely-taken "re-clean leftover buffers" path (dirty buffers reappearing after truncation) AND concurrent rename/unlink of the truncated file β a narrow race with low impact (dmesg disclosure of freed-heap bytes). Not PoC'd: the trigger needs a contrived buffer reappearance race; filed as Low with a compile- validated fix.
Fix (compile-validated in the guest kernel build; see DF-5807/5811 pack runs)
Snapshot the name bytes under the spin instead of the pointer (fix.diff). Alternatively hold a namecache reference across the loop.
DF-5810 β VERDICT
Status: untested (Low-severity race; no PoC run β skip per contract for Low findings).
Code-level proof of the protocol violation: - pointer captured under spin: sys/kern/vfs_subr.c:531-534 - spin released: :534 - blocking re-clean loop with sleeps: :541-554 (BUF_TIMELOCK/bwrite) - dereference: :551 kprintf("%s", filename) - nc_name is separately allocated and freed with its ncp: sys/sys/namecache.h:136, cache_zap() frees the ncp+name without consulting vp->v_spin holders after they release it.
The fix.diff (strlcpy into a 256-byte local under the spin) compiled as part of the patched guest kernel built for DF-5807/5811 validation (the same vfs_subr.c object), but no runtime trigger was attempted.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff compiles (part of the validated kernel build) but no runtime trigger exists for this narrow race
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
code-path analysis in VERDICT.md; fix.diff compile-validated as part of the guest kernel build used for DF-5807/5811 fix validation
PoC changes
n/a - no PoC authored
Verified recommended fix
snapshot the name bytes under v_spin instead of the pointer (fix.diff, applies cleanly, compile-validated)
Verdict
vtruncbuf() snapshots the separately-allocated nc_name pointer under vp->v_spin (vfs_subr.c:531-534), releases the spin, blocks in the re-clean RB_SCAN loop (BUF_TIMELOCK/bwrite), then dereferences the pointer at vfs_subr.c:551 kprintf - a rename/unlink racing the truncation's re-clean path can cache_zap the ncp and free nc_name in the window, giving a use-after-free read of freed heap into dmesg. Low severity (narrow race, console-only output); no PoC run per contract skip rule for Low findings.
No comments yet.