pccard_attach_card re-entry on card-detect bounce: previous function list orphaned (unbounded kernel memory leak) and duplicate children stacked over live drivers
Summary
No already-attached guard: the cbb event thread re-runs the insert path while the card is present and a CD glitch inside the 1s debounce is seen as present - STAILQ_INIT orphans the previous pf/cfe chains (hundreds of KB/cycle permanent leak, free path only walks the current list), device_add_child stacks duplicates over live instances, final removal leaves zombie devices over destroyed windows. Evil card wiggling CD1#/CD2# every ~2s = unbounded kmem exhaustion. Hardware-gated (static trigger trace). pf_head-non-empty EBUSY guard fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5356 Β· 21 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rig2_main.c | β | 18.2 KB | view raw | |
| rig2_shims.h | β | 13.8 KB | view raw | |
| extract2.sh | β | 8.1 KB | view raw | |
| build.sh | β | 66 B | view raw | |
| run.sh | β | 123 B | view raw | |
| run_matrix.sh | β | 1.9 KB | view raw | |
| run.log | β | 8.0 KB | view raw | |
| fixed_run.log | β | 1.1 KB | view raw | |
| build.log | β | 2.5 KB | view raw | |
| extract2_manifest.txt | β | 2.4 KB | view raw | |
| matrix.log | β | 1.6 KB | view raw | |
| env.txt | β | 323 B | view raw | |
| fix.diff | β | 601 B | view raw | |
| fixes/ | β | 4.0 KB | β download | |
| VERDICT.md | β | 6.3 KB | β raw | |
| README.md | β | 1.6 KB | β raw | |
| verdict.json | β | 6.2 KB | view raw | |
| postboot_run.log | β | 16.8 KB | view raw | |
| postboot_extract_manifest.txt | β | 2.4 KB | view raw | |
| kernel_apply.log | β | 231 B | view raw | |
| kernel_build.tail.log | β | 4.6 KB | view raw |
DF-5356 -- pccard_attach_card re-entry on card-detect bounce (leak + duplicate children)
Impact: unbounded kernel memory leak (376+ bytes per CD bounce cycle, linear growth), zombie device tree with duplicate driver instances over destroyed bridge windows, leaked I/O + CCR windows (which prime DF-5355's busy-panic). Physical-access kmem-exhaustion DoS driven by an evil card wiggling CD1#/CD2# every ~2 s.
Reproduce (verbatim rig -- shared with DF-2318/DF-5355)
# on the DragonFly guest (pristine /usr/src): sh build.sh # machine-extracts production pccard.c/cis/subr_bus code ./rig2 reentry_leak # the DF-5356 scenario (51 CD-bounce cycles) ./rig2 benign_cycle # negative control (attach/detach, zero leak) sh run_matrix.sh matrix
Success criterion (baseline):
[after attach #2 (bounce)] ORPHANED: pf=1 cfe=1 (leak=376 bytes) | zombies=2
TOTAL after 51 bounce cycles: orphaned-kmalloc-blocks=102 leak=19176 bytes |
zombie-children=51 | io-held=28 ccr-held=28
With the fix (fixed_run.log / postboot): TOTAL ... blocks=0 leak=0
zombies=0 io-held=0 ccr-held=0, while benign_cycle still attaches 51 times.
The full rig lives in ../DF-2318/; this pack is a self-contained copy.
Fix
fix.diff (== fixes/df5356.diff): EBUSY guard at the top of
pccard_attach_card when sc->card.pf_head is non-empty (the removal path
empties the list at pccard.c:317-324, keeping the marker correct). The
originally-seeded hunk was malformed and was regenerated; validated by rig
A/B + combined-fix kernel boot. Never applied to sys/.
DF-5356 -- VERDICT
Verdict in one line
REPRODUCED (verbatim-rig level; deterministic, linear unbounded growth) --
impact: dos (unbounded kernel-memory leak + device-tree corruption).
pccard_attach_card has no already-attached guard: a card-detect bounce that
re-runs the bridge insert path while the card is present re-enters it with no
intervening detach; STAILQ_INIT at pccard.c:189 (and again inside
pccard_read_cis, pccard_cis.c:104) orphans the previous pf/cfe chains
(permanent kmalloc leak), and device_add_child at :256 stacks duplicate
children over live driver instances. The kernel entry is hardware-gated (no
PCMCIA bridge in QEMU -- env.txt); the software behavior is proven on the
byte-identical extracted production code.
What was executed
Same verbatim rig as DF-2318/DF-5355 (extract2.sh, sha256-gated,
byte-identical pccard_attach_card/pccard_detach_card/pccard_read_cis
chain + ledgered kmalloc + tracked device tree). Scenario reentry_leak
drives the exact production re-entry sequence the cbb event thread produces
on a CD glitch (pccbb.c:621-627 clears CBB_CARD_OK and wakes the thread; the
thread's 1 s debounce at pccbb.c:479-483 still sees the card present;
cbb_insert at :458-461 -> exca_insert at exca.c:759-764 ->
CARD_ATTACH_CARD unconditionally -- no layer checks whether the card is
already attached):
attach #1 (card inserted) -> pf/cfe chain A, child 0, driver attached
attach #2 (CD bounce, NO detach) -> STAILQ_INIT orphans chain A,
child 1 created OVER THE LIVE CARD,
both drivers running
detach (final removal) -> walks only the CURRENT list (pccard.c:303,
:317-324): chain B freed, chain A and
child 0 survive forever
x 51 cycles
The measured damage (run.log)
[after attach #2 (bounce)] ORPHANED: pf=1 cfe=1 (leak=376 bytes) | zombies=2 | io-held=2 ccr-held=2
[after detach] ORPHANED: pf=1 cfe=1 (leak=376 bytes) | zombies=1 | io-held=1 ccr-held=1
TOTAL after 51 bounce cycles: orphaned-kmalloc-blocks=102 leak=19176 bytes |
zombie-children=51 | io-held=28 ccr-held=28
- 376 bytes permanently leaked per bounce cycle (pf 216 + cfe 160, canary-verified live-and-unreachable from the current list) -- exactly the per-cycle cost of a single-function card; multi-function / multi-CFE cards cost hundreds of KB per cycle (finding's estimate), and the growth is linear and unbounded (the leak continues even after the bridge windows are exhausted: cycles past window exhaustion still orphan pf/cfe chains).
- 51 zombie children, each a
device_add_childover an orphaned function whose driver instance stays attached in production; after final removal the duplicates survive over destroyed bridge windows. - 28 leaked I/O windows + 28 leaked CCR mappings: the orphaned rl
entries keep
resbusy forever (the free path only walks the current list), so the bridge windows are never returned -- this is also the priming condition for DF-5355's busy-panic on subsequent inserts.
Negative control benign_cycle (attach -> detach, no bounce, x51):
leak=0, zombies=0, io-held=0 -- the leak is exactly the no-detach
re-attach, not the attach/detach machinery.
Fix validation
- Single-fix A/B (matrix.log):
if (!STAILQ_EMPTY(&sc->card.pf_head)) return (EBUSY);at the top ofpccard_attach_card-> reentry_leak TOTAL becomes 0/0/0/0 across all 51 cycles; benign_cycle (attach/detach x51) still works (the removal path empties the list at :317-324, keeping the marker correct); no_vers1 (DF-2318) and busypanic (DF-5355) scenarios unchanged under this variant. - Combined three-fix kernel:
-Werrornativekernel rc=0, installed, booted (kernel #1, sha256 4faad36e...); rig re-extracted from the RUNNING kernel's source (--allow-patched) and re-driven: fixed_run.log shows 0 leak / 0 zombies / 0 held windows over all 51 bounce cycles while the healthy attach/detach path still works. Guest reset to the clean-source snapshot afterwards.
NOTE on the seeded fix.diff: the originally-seeded hunk was malformed
(patch: malformed patch at line 19) and placed declarations after
statements; it was regenerated with the same semantics (EBUSY guard) placed
after the declaration block -- this pack's fix.diff is the validated one.
Kernel references
- sys/bus/pccard/pccard.c:189 (STAILQ_INIT orphans the previous list)
- sys/bus/pccard/pccard.c:256 (device_add_child stacks duplicates)
- sys/bus/pccard/pccard.c:268 (pccard_function_init over the orphaned card)
- sys/bus/pccard/pccard.c:303 / :317-324 (detach walks/frees CURRENT list only)
- sys/bus/pccard/pccard_cis.c:104 (second STAILQ_INIT inside pccard_read_cis)
- sys/dev/pccard/pccbb/pccbb.c:458 (cbb_insert on every present-wake)
- sys/dev/pccard/pccbb/pccbb.c:479 (1 s debounce still sees present)
- sys/dev/pccard/pccbb/pccbb.c:621 (CD event clears CARD_OK, wakes thread)
- sys/dev/pccard/exca/exca.c:759 (exca_insert -> CARD_ATTACH_CARD, no guard)
Exploit chain
evil card grounds CD1#/CD2# -> card attaches -> card ungrounds CD briefly (CSC interrupt; pccbb.c:621-627 clears CBB_CARD_OK and wakes the event thread) -> card regrounds CD -> thread debounce (:479-483) observes present -> cbb_insert -> exca_insert -> CARD_ATTACH_CARD -> pccard_attach_card with no intervening detach -> previous pf/cfe chain orphaned (376+ bytes/cycle permanent kmalloc leak; linear, unbounded), duplicate children stacked over live drivers, bridge I/O + CCR windows leaked -> repeat every ~2 s: kernel memory exhaustion (kmem malloc zone pressure) + zombie device tree; window exhaustion also primes DF-5355's busy-panic on the next insert. The kernel's own comment at pccbb.c:562-567 treats CD jitter as routine.
Environment
Same guest/rig as DF-2318 (env.txt). Artifacts: run.log (baseline decisive, 51-cycle TOTAL), fixed_run.log (running-kernel fixed source, 0 leak), matrix.log, build.log, extract2_manifest.txt.
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
fixedVALIDATED. (1) Single-fix rig A/B (matrix.log): reentry_leak TOTAL becomes 0 orphaned / 0 bytes / 0 zombies / 0 held windows across all 51 bounce cycles; benign_cycle (attach->detach x51) still attaches cleanly every cycle, proving the guard does not break normal insert/remove; no_vers1 (DF-2318) and busypanic (DF-5355) scenarios unchanged under the f5356 variant. (2) Combined three-fix kernel: -Werror nativekernel rc=0, installkernel rc=0, booted kernel #1 (sha256 4faad36e...); rig re-extracted from the RUNNING kernel's patched source (--allow-patched) and re-driven: fixed_run.log shows 0 leak / 0 zombies / 0 held over 51 bounce cycles. In-kernel behavioral test impossible (no PCMCIA bridge).
fix.diff; fixed_run.log; matrix.log; postboot_extract_manifest.txt + postboot_run.log + kernel_apply.log + kernel_build.tail.log (in DF-2318 pack)
Confirmed kernel references
Detail
Exploit chain
evil card grounds CD1#/CD2# -> card attaches -> card ungrounds CD briefly (CSC intr, pccbb.c:621-627 clears CBB_CARD_OK, wakes event thread) -> card regrounds CD -> thread debounce pccbb.c:479-483 observes present -> cbb_insert :458 -> exca_insert exca.c:759 -> CARD_ATTACH_CARD -> pccard_attach_card re-entered, no detach ran -> STAILQ_INIT (pccard.c:189, pccard_cis.c:104) orphans previous pf/cfe chain (376+ bytes/cycle permanent kmem leak, linear, unbounded) + device_add_child (:256) stacks duplicate children over live drivers -> bridge I/O + CCR windows leaked (busy rl entries never released) -> repeat ~every 2 s: kmem exhaustion + zombie device tree over destroyed windows; window exhaustion primes DF-5355 busy-panic on next insert
Evidence (decisive lines)
run.log (baseline decisive: per-cycle cycle-0 trace + 'TOTAL after 51 bounce cycles: orphaned-kmalloc-blocks=102 leak=19176 bytes | zombie-children=51 | io-held=28 ccr-held=28'); fixed_run.log (running-kernel fixed source: 0/0/0/0 across all 51 cycles); matrix.log (f5356 variant flips exactly reentry_leak; benign_cycle + no_vers1 + busypanic unchanged); extract2_manifest.txt; build.log; DF-2318 pack run.log/run.2.log/run.3.log (determinism incl. benign_cycle zero-leak control)
PoC changes
No seed PoC existed (pack was a static trace only). Wrote the reentry_leak scenario in the verbatim lifecycle rig (shared with DF-2318/DF-5355): CD-bounce re-attach driven through the byte-identical attach/detach chain with ledgered kmalloc, tracked device tree, and io/ccr window accounting; 51-cycle amplifier plus benign_cycle negative control. The seeded fix.diff was malformed (patch: 'malformed patch at line 19', declarations after statements) and was regenerated with the same EBUSY-guard semantics placed correctly.
Verified recommended fix
Guard pccard_attach_card against re-entry: return EBUSY when sc->card.pf_head is non-empty (the removal path empties the list at pccard.c:317-324, keeping the marker correct across insert/remove cycles); validated by rig A/B and combined-fix kernel.
Verdict
REPRODUCED at the verbatim-rig level (deterministic, linear unbounded growth), upgrading the seeded static-only verification to executed evidence. The production pccard_attach_card/pccard_detach_card/pccard_read_cis chain was machine-extracted BYTE-IDENTICAL from the guest's /usr/src (sha256 gates; PCCARD_BYTE_IDENTICAL=yes) and driven through the exact re-entry sequence the cbb event thread produces on a card-detect bounce (pccbb.c:621-627 -> :479-483 -> :458-461 -> exca.c:759-764 -> CARD_ATTACH_CARD, no already-attached guard anywhere): attach, re-attach with NO intervening detach, final detach, x51 cycles. Measured damage per cycle: 376 bytes (pf 216 + cfe 160) permanently orphaned by the STAILQ_INIT at pccard.c:189 (and pccard_cis.c:104) -- the detach free path walks only the CURRENT list (pccard.c:303, :317-324); plus one zombie child (device_add_child at :256 stacking a duplicate driver instance over the live card); plus one leaked bridge I/O window and one leaked CCR mapping (orphaned rl entries keep res busy forever). 51 bounce cycles: 102 orphaned blocks / 19176 bytes / 51 zombies / 28 held io + 28 held ccr (window pool exhausts; the pf/cfe leak continues linearly regardless) -- unbounded kernel-memory exhaustion at ~2 s CD-glitch periods, and the leaked busy windows prime DF-5355's panic on subsequent inserts. Negative control benign_cycle (attach->detach x51, no bounce): leak=0/zombies=0/held=0, isolating the defect to the no-detach re-attach. Kernel entry itself is hardware-gated (no PCMCIA bridge in QEMU, env.txt) -- DF-1040/1041/2319 precedent; the trigger trace pccbb.c:458/479/621 + exca.c:759 was verified line-by-line in the guest's own tree.
No comments yet.