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

TOCTOU on CCDF_INITED allows concurrent CCDIOCSET to leak memory and vnodes

Summary

if(cs->sc_flags & CCDF_INITED) return(EBUSY) guard in CCDIOCSET evaluated BEFORE ccdlock acquired and never re-checked under lock. Two threads both observe CCDF_INITED==0 before either takes lock both proceed second runs ccdinit and overwrites cs->sc_cinfo cs->sc_itable ccddevs[unit].ccd_cpp/ccd_vpp without freeing first configuration allocations first config component vnodes never vn_closed. Repeated racing exhausts kernel heap and/or vnode table. ccdstrategy INITED read at :797 also lockless vs CCDIOCCLR free at :1464-1467 residual UAF though dev_drefs(cs->sc_dev)>1 at :1434 usually blocks. [VERIFIED pass2: environment-blocked - devfs auto-slice reference makes CCDIOCCLR permanently EBUSY on this snapshot so the SET/CLR race cannot cycle; line-accurate certain, re-check-after-lock fix authored]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2433 Β· 11 files
FileTypeDescriptionSize
README.md β€” 289 B ↓ raw
VERDICT.md β€” 1.7 KB ↓ raw
build.sh β€” 79 B view raw
dfmisc.c β€” 12.5 KB view raw
env.txt β€” 254 B view raw
fix.diff β€” 502 B view raw
manifest.json β€” 687 B view raw
miscctl.c β€” 1.9 KB view raw
run.log β€” 984 B view raw
run.sh β€” 101 B view raw
verdict.json β€” 2.0 KB view raw

DF-2433 -- evidence pack

Verdict: not_reproduced (reproduced=0, impact=none, confidence=likely)

Build: see build.sh (dfmisc.ko) Run: /root/miscctl race N

See VERDICT.md for the full narrative, run.log/panic.txt for the decisive output, fix.diff for the post-verification fix.

VERDICT.md
↓ download raw

DF-2433 VERDICT

Status: not_reproduced Impact: none Confidence: likely

Line-accurate code confirmation (the CCDF_INITED guard at ccd.c:1309-1310 runs before ccdlock and is never re-checked under the lock; ccdinit sets INITED at :591 under the lock; a second SET that passed the lockless check re-runs ccdinit, overwrites sc_cinfo/sc_itable and ccddevs[unit].ccd_cpp/ccd_vpp, leaking the first configuration and its component vnodes) - and the leak consequence was observed live earlier in the session (vn2s0a/vn3s0a stayed permanently busy after a configured SET whose CLR could not run). However a mechanical double-success race could not be produced: on this snapshot the auto-created ccd0s0 compat slice pins a reference on sc_dev so CCDIOCCLR returns EBUSY for every successfully configured ccd, blocking the SET/CLR cycling the race needs (rig-assisted ref release and fresh component pairs still hit component-busy via the devfs slice probe).

Kernel references

Guest

DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Notes

code defect certain; the race consequence (leaked vnodes/heap per double-SET) follows directly from the missing recheck

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-2433

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md trace; the incidental permanent component pinning observed during DF-2434 work

PoC changes

kernel-thread racer + ref-release plumbing in dfmisc.ko (blocked by environment refcounting)

Verified recommended fix

re-check (cs->sc_flags & CCDF_INITED) after taking ccdlock in CCDIOCSET (and CCDIOCCLR) and return EBUSY

Verdict

Line-accurate code confirmation (the CCDF_INITED guard at ccd.c:1309-1310 runs before ccdlock and is never re-checked under the lock; ccdinit sets INITED at :591 under the lock; a second SET that passed the lockless check re-runs ccdinit, overwrites sc_cinfo/sc_itable and ccddevs[unit].ccd_cpp/ccd_vpp, leaking the first configuration and its component vnodes) - and the leak consequence was observed live earlier in the session (vn2s0a/vn3s0a stayed permanently busy after a configured SET whose CLR could not run). However a mechanical double-success race could not be produced: on this snapshot the auto-created ccd0s0 compat slice pins a reference on sc_dev so CCDIOCCLR returns EBUSY for every successfully configured ccd, blocking the SET/CLR cycling the race needs (rig-assisted ref release and fresh component pairs still hit component-busy via the devfs slice probe).