β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2528

Unlocked vn_softc state in vnstrategy/vnget races vnclear -> NULL deref or UAF

Summary

struct vn_softc has no lock whatsoever. vnstrategy and vnget read vn->sc_vp/vn->sc_object after only unlocked peek at VNF_INITED. vnclear invoked from VNIOCDETACH and MOD_UNLOAD clears VNF_INITED calls vn_close/vm_pager_deallocate then NULLs pointers. Concurrent strategy call observes stale non-NULL pointer being freed or re-reads sc_vp as NULL dereferences in vn_lock/VOP_GETATTR kernel panic or UAF on recycled vnode. No serialization between paths. disk_getopencount unlocked read opencount>1 gate racy. VNIOCDETACH needs SYSCAP_RESTRICTEDROOT strategy needs operator-group open 0640. Privileged-cooperator local DoS. [VERIFIED pass2: not hit in 8 root hammer rounds - source trace stands no lock on vn_softc state across strategy vs vnclear; state-token fix compile-validated]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2528 Β· 8 files
FileTypeDescriptionSize
README.md β€” 939 B ↓ raw
VERDICT.md β€” 1.6 KB ↓ raw
build.sh β€” 99 B view raw
run.log β€” 108 B view raw
env.txt β€” 963 B view raw
fix.diff β€” 1.7 KB view raw
manifest.json β€” 663 B view raw
verdict.json β€” 2.1 KB view raw

DF-2528 -- Unlocked vn_softc state races vnclear (NULL deref / UAF)

Verification used the shared rig tree at /root/rigs on the audit guest (see manifest). The rig for this finding: None (rig.c = harness, gen.sh = verbatim extractor, dfv_*_extract.h = generated production text). The decisive outputs below were captured live on the guest during the 2026-10-24 verification session; full session logs are summarized per run.

Build

sh build.sh            # extracts verbatim driver text + builds the KLD

Run (guest, root)

kldload ./dfv_rig.ko

(for i in 1..8; do dd if=/dev/vn3 of=/dev/null bs=64k count=4; done) racing (vnconfig -u vn3 x8)

Expected

no crash in the hammer; vn module builds with the token fix.

Fix validation

fix.diff adds sc_iotok and takes it across the strategy state snapshot and the vnclear teardown; compile-validated.

Verdict: not_reproduced / impact=none (see verdict.json, VERDICT.md)

VERDICT.md
↓ download raw

DF-2528 verdict -- Unlocked vn_softc state races vnclear (NULL deref / UAF)

status: not_reproduced reproduced: 0 impact: none confidence: likely attempts: 1 runtime_sec: 400

A root-driven hammer (raw dd I/O on vn3 racing 8 vnconfig -u detach rounds) produced no panic - the window is narrow and the opencount gate serializes the common case. The source trace stands: struct vn_softc carries no lock; vnstrategy/vnget peek VNF_INITED/sc_vp unlocked while vnclear (VNIOCDETACH, RESTRICTEDROOT) closes the vnode, deallocates the object and NULLs the pointers (:744-756); disk_getopencount is an unlocked read. Privileged-cooperator local DoS as filed. Fix (state token serializing the strategy snapshot against vnclear) compile-validated in the vn module build; the full vnode-lifetime fix additionally needs a held reference across I/O (noted in the diff).

Evidence (decisive lines)

HAMMER_DONE - no panic, guest alive, vn3 detached cleanly (race not hit) vn module with token fix: BUILD OK

Exploit chain / privesc assessment

root+operator cooperation required; ceiling: panic/UAF on a racing detach.

Fix + A/B

fix.diff adds sc_iotok and takes it across the strategy state snapshot and the vnclear teardown; compile-validated.

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_testable
↓ fix.diffper-fix-DF-2528

Confirmed kernel references

Detail

Exploit chain

root+operator cooperation required; ceiling: panic/UAF on a racing detach.

Evidence (decisive lines)

no crash in the hammer; vn module builds with the token fix.

PoC changes

hammer only; the deterministic race driver was not built (finding is Low, privileged race).

Verified recommended fix

serialize vnstrategy state snapshot against vnclear with a token

Verdict

A root-driven hammer (raw dd I/O on vn3 racing 8 vnconfig -u detach rounds) produced no panic - the window is narrow and the opencount gate serializes the common case. The source trace stands: struct vn_softc carries no lock; vnstrategy/vnget peek VNF_INITED/sc_vp unlocked while vnclear (VNIOCDETACH, RESTRICTEDROOT) closes the vnode, deallocates the object and NULLs the pointers (:744-756); disk_getopencount is an unlocked read. Privileged-cooperator local DoS as filed. Fix (state token serializing the strategy snapshot against vnclear) compile-validated in the vn module build; the full vnode-lifetime fix additionally needs a held reference across I/O (noted in the diff).