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)
PoC verification
Evidence pack
findings/poc/DF-2433 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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.
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_testableConfirmed 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).
No comments yet.