Unbounded resource-data ingestion: no-END stream wedges the kernel with unbounded kmalloc growth; endless logical-device tags amplify ~164Γ into permanent kernel memory
Summary
pnp_read_resources loops until END with no total-length cap (never-ENDING card = kldload/boot thread wedged forever, single allocation 5.57MB at t+50s -> 12.4MB at t+125s still growing); pnp_create_devices creates a device + 8640B configs[] + never-freed csnldn per LD tag with no cap (36KB stream -> +5.9MB permanent M_DEVBUF, 6000 children). Both faces pnprig-reproduced live; 64KiB-data + 256-LDN caps in the validated fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5445 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 2.1 KB | β raw | |
| verdict.json | β | 4.6 KB | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| run_endless.log | β | 1.6 KB | view raw | |
| run_ldspray.log | β | 750 B | view raw | |
| serial_endless_full.log | β | 2.8 KB | view raw | |
| fix_run_endless.log | β | 219 B | view raw | |
| fix_run_ldspray.log | β | 643 B | view raw | |
| env.txt | β | 382 B | view raw |
DF-5445 β unbounded resource-data ingestion in pnp_read_resources()/pnp_create_devices() (sys/bus/isa/pnp.c)
What
Two unbounded-ingestion faces, both card-controlled:
-
No-END stream:
pnp_read_resources()(pnp.c:536-581) loops until an END tag or a read error. A card that keeps the resource-data-ready bit asserted forever (and never sends tag 0x78) keeps the loop running andpnp_read_bytes()growing the kmalloc buffer in 1 KiB steps β no cap on total length β until kernel memory is exhausted. The kldload/boot thread spins forever (kernel wedge). -
Endless logical devices: each PNP_TAG_LOGICAL_DEVICE processed by
pnp_create_devices()(pnp.c:428-460) allocates a device + ivars +csnldn(24 B, also never freed) andpnp_parse_resources()kmallocs 9 Γ sizeof(struct isa_config) = 8640 B per slice. ~6 bytes of card data β >1 KB of permanent kernel allocation (measured 164Γ amplification);ldnis an unbounded int even though the LDN register is 8-bit.
Rig
Same rig/ as DF-5443. Module hack_dfc (script 3): card synthesizes an
endless tag stream (no END). Module hack_dfd (script 4): card sends 6000
logical-device tags + END (finite amplification demo).
Build / Run
(cd /usr/src/sys/bus/isa/hack_dfc && make obj && make depend && make) (cd /usr/src/sys/bus/isa/hack_dfd && make obj && make depend && make) timeout 50 ssh ... 'kldload .../rigdfc.ko' # never returns (RC=124) kldload .../rigdfd.ko # returns; vmstat -m diff
Expected
Baseline:
* rigdfc: kldload never returns (local timeout RC=124); serial log
shows kmalloc growth without bound (5.57 MB single allocation at t+50 s,
12.4 MB at t+125 s, still growing); guest degraded.
* rigdfd: M_DEVBUF 208Kβ6.11 MB permanent (+5.9 MB for a 36 KB card
stream), 211β11.9K chunks, 6000 children.
Fixed kernel (fix.diff: PNP_MAX_RESOURCE_DATA = 64 KiB cap in
pnp_read_resources, 256-LDN cap in pnp_create_devices):
* rigdfc: kldload returns quickly with "too much resource data".
* rigdfd: capped at 256 logical devices, "too many logical devices".
DF-5445 VERDICT β REPRODUCED (kernel wedge / unbounded memory exhaustion; DoS)
Bottom line
Both unbounded-ingestion faces demonstrated live on the baseline kernel; both eliminated by the caps in fix.diff.
Face 1 β no-END stream (wedge)
Card keeps resource-data-ready asserted forever, never sends tag 0x78.
pnp_read_resources() (pnp.c:546-574) has no total-length cap;
pnp_read_bytes() grows the buffer in 1 KiB steps without bound.
Observed (baseline #0):
kldload rigdfc.koβ never returns (50 s local timeout, RC=124),- kmalloc growth: single allocation 5.57 MB at t+50 s β 12.41 MB at t+125 s, still climbing (verbatim samples in run_endless.log),
- parallel
vmstatssh starved (RC=124); guest never recovers β reset required.
Face 2 β logical-device spray (permanent memory amplification)
6000 PNP_TAG_LOGICAL_DEVICE tags (36 KB stream, rigdfd.ko):
M_DEVBUF 208K β 6.11 MBpermanent (+5.9 MB), chunks 211 β 11.9K (run_ldspray.log) β ~164Γ amplification, unbounded (ldnis an int; the LDN register is 8-bit; csnldn at pnp.c:453 is never freed);- 6000 children created,
found=1, load returns.
Fix validation (kernel #1)
- Endless stream: completes in 4.26 s,
pnp_read_resources: too much resource dataΓ7,RC=0, guest healthy (fix_run_endless.log). - LD spray: stops at 256 logical devices β
isa0: too many logical devices, devbuf delta +258 K vs +5.9 MB baseline (fix_run_ldspray.log).
Fix
fix.diff: PNP_MAX_RESOURCE_DATA 64 KiB cap in pnp_read_resources()
+ 256-LDN cap in pnp_create_devices(). Build log: DF-5443/fix_build.log.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
fixedSingle-fix kernel (fix.diff, #1 Mon Oct 5 16:47:13): the exact wedge stream completes in 4.26 s with 'pnp_read_resources: too much resource data' (RC=0, guest healthy, no unbounded growth); the LD spray stops at 256 devices with 'isa0: too many logical devices' and devbuf delta +258K vs +5.9MB baseline. Baseline bad behavior GONE on both faces.
fix_run_endless.log, fix_run_ldspray.log; fix_build.log + fix.diff (in DF-5443 pack)
Confirmed kernel references
Detail
Exploit chain
crafted ISA-PnP card -> endless ready-or-data byte service, no END tag -> pnp_read_resources infinite loop -> pnp_read_bytes unbounded kmalloc growth (1 KiB steps) -> kernel-memory exhaustion + permanently busy kldload/boot thread (wedge). Alternate face: endless LOGICAL_DEVICE tags -> per-tag device + ivars + csnldn (leaked) + 8640-byte configs[] allocation -> unbounded permanent kernel memory from a ~6-bytes-per-device card stream.
Evidence (decisive lines)
run_endless.log (SSH_RC=124, verbatim kmalloc growth samples 5566464..5570560 at t+50s and 12407808..12409856 at t+125s, starved second command, reset required); run_ldspray.log (baseline: devbuf 208K->6.11MB, 211->11.9K chunks, found=1, 6.57s load); fix_run_endless.log (patched: 4.26 s, RC=0, 7x 'too much resource data'); fix_run_ldspray.log (patched: 'isa0: too many logical devices', devbuf +258K); rig/ (hack_dfc endless wrapper, hack_dfd spray wrapper)
PoC changes
Same rig as DF-5443 (see its poc_changes). Script 3 synthesizes an endless 0x09/0x00 small-tag stream; script 4 emits 6000 finite LD tags + END.
Verified recommended fix
Cap total ingested resource bytes per card (PNP_MAX_RESOURCE_DATA=64 KiB) in pnp_read_resources() and cap logical devices at 256 (LDN register width) in pnp_create_devices() β full unified diff in fix.diff.
Verdict
REPRODUCED on DragonFly 6.5-DEVELOPMENT (baseline #0), both faces. Face 1 (no-END stream): a card that keeps resource-data-ready asserted and never sends tag 0x78 keeps pnp_read_resources() (pnp.c:546-574) looping and pnp_read_bytes() growing the kmalloc buffer in 1 KiB steps with NO total-length cap β kldload NEVER returned (local timeout RC=124 after 50 s), the buffer reached a single 5.57 MB allocation at t+50 s and 12.41 MB at t+125 s and was still growing, a parallel vmstat ssh starved (RC=124), and the guest never recovered (reset required): kernel wedge + unbounded kernel-memory exhaustion. Face 2 (logical-device spray): 6000 PNP_TAG_LOGICAL_DEVICE tags (36 KB card stream) create 6000 bus children, each with an 8640-byte configs[] kmalloc inside pnp_parse_resources() plus ivars and a never-freed 24-byte csnldn β measured M_DEVBUF growth 208K->6.11MB (+5.9MB permanent, 211->11.9K chunks) for the 36 KB stream (~164x amplification); ldn is an unbounded int although the LDN register is 8-bit, so the face is unbounded. FIX VALIDATED on rebuilt kernel #1 (fix.diff: PNP_MAX_RESOURCE_DATA=64 KiB cap in pnp_read_resources + 256-LDN cap in pnp_create_devices): the exact wedge stream now completes in 4.26 s with 'pnp_read_resources: too much resource data' and kldload RC=0 (guest healthy); the LD spray stops at 'isa0: too many logical devices' with devbuf delta +258K (vs +5.9MB baseline).
No comments yet.