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

cardbus_cis: decode_tuple_bar keeps the attacker-controlled 32-bit CIS BAR size (truncated to a negative int) and passes it unchecked as the resource count; 1<<(ffs(0x80000000)-1) = 1<<31 signed-overflow UB also yields a negative count β€” both beyond DF-1811's ffs(0) case

Summary

The raw CIS 32-bit size field is assigned to an int len with no upper-bound rejection; when the probe reads back 0/0xffffffff (exactly the values the sanity check excludes) the attacker's value survives to resource_list_add: 0xFFFFFFF0 -> count -16; independently 1<<31 overflows to INT_MIN. Negative counts in a cardbus child's resource list are nonsense for every later rman allocation. Hardware-gated (crafted CardBus CIS); harness REPRODUCES all three modes (fix validated -> EINVAL). Reject-len<=0 + masked-probe-zero fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4690 Β· 14 files
FileTypeDescriptionSize
cardbus_harness.c β€” 7.2 KB view raw
build.sh β€” 85 B view raw
run.sh β€” 109 B view raw
fix_validate.sh β€” 1.5 KB view raw
fix4690.diff β€” 626 B view raw
fix4691.diff β€” 722 B view raw
build.log β€” 577 B view raw
run.log β€” 922 B view raw
fix_run.log β€” 2.8 KB view raw
env.txt β€” 376 B view raw
fix.diff β€” 626 B view raw
VERDICT.md β€” 1.9 KB ↓ raw
manifest.json β€” 764 B view raw
verdict.json β€” 3.4 KB view raw
VERDICT.md
↓ download raw

DF-4690 -- verdict narrative

Claim

decode_tuple_bar keeps the attacker-controlled 32-bit CIS BAR size when the PCI probe fails (pci_bar == 0 || pci_bar == 0xffffffff, cardbus_cis.c:362) and assigns it to the INT parameter len (:308) -> values >= 2^31 truncate to negative; independently, a BAR probing 0x80000000 gives 1 << (ffs(0x80000000)-1) = 1 << 31 (:368) -- signed overflow UB that manifests as INT_MIN. Both reach resource_list_add(:377) as a NEGATIVE count for the child's resource list entry.

Reproduced?

YES, decoder-level (verbatim extraction, run on the DragonFly guest; the accepted DF-1811 used the same source-only standard because no CardBus bridge exists on the guest).

  • mode 1: cis size field 0xFFFFFFF0, probe 0xffffffff -> count = -16 (0xfffffff0)
  • mode 2: probe 0x80000000 -> ffs==32 -> 1<<31 UB -> count = -2147483648 (0x80000000)
  • mode 3 (contrast = known DF-1811): masked probe 0 -> ffs(0)-1 UB -> INT_MIN

A negative count in a resource-list entry is nonsense downstream (rman allocation for the child driver); the realistic ceiling is the card's own driver failing to attach / resource misbehavior -- hardware-gated, no local or remote trigger. Severity Low.

Fix validation (fix_validate.sh, fix.diff = fix4690.diff)

Baseline: negative counts reach resource_list_add (above). Patched (reject pci_bar==0 after mask [the DF-1811 half] and len<=0 before resource_list_add): all three cases return EINVAL and the captured count keeps its sentinel 0x42424242 -- resource_list_add is never reached. FIXED at decoder level.

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

fixed
baseline reproduced→ patch + rebuild →patched clean

Harness-level: baseline m1/m2/m3 deliver negative counts into resource_list_add; with fix4690.diff all three return EINVAL and the captured count keeps its sentinel 0x42424242 (never called).

run.log: 'm1: ... probe=0xffffffff -> resource_list_add count = -16 ... NEGATIVE COUNT' and 'm2: ... probe=0x80000000 -> count = -2147483648 ... NEGATIVE'; fix_run.log: 'PATCHED modes 1/2/3 ... count = 1111638594 (0x42424242)' (sentinel intact = resource_list_add never reached).
↓ fix.diffper-fix-DF-4690

Confirmed kernel references

Detail

Exploit chain

malicious CardBus card (physical insertion) -> cardbus_do_cis -> decode_tuple_bar/decode_tuple_funce -> negative resource count / stale-byte MAC ivar; no uid0 path

Evidence (decisive lines)

run.log: 'm1: ... probe=0xffffffff -> resource_list_add count = -16 ... NEGATIVE COUNT' and 'm2: ... probe=0x80000000 -> count = -2147483648 ... NEGATIVE'; fix_run.log: 'PATCHED modes 1/2/3 ... count = 1111638594 (0x42424242)' (sentinel intact = resource_list_add never reached).

PoC changes

verbatim extraction of the two decoder bodies (anchors, cardbus_cis.c:258-290/293-385) against minimal newbus/pci stubs (pci_read/write_config probe emulation, resource_list_add capture); added errno.h include after first build failed on EINVAL

Verified recommended fix

After the probe block, reject non-positive sizes (len <= 0 -> EINVAL) and the masked pci_bar == 0 case (the DF-1811 half) before resource_list_add.

Verdict

REPRODUCED at decoder level on the DragonFly guest (verbatim-extracted decode_tuple_bar, cardbus_cis.c:293-385, driven with crafted CIS bytes + emulated PCI probe; the accepted DF-1811 used the same source-only standard since the guest has no CardBus bridge). Beyond DF-1811 (which covers only masked-probe-zero ffs(0)): (mode 1) when the BAR probe reads back 0xffffffff -- exactly the case :362 excludes -- the attacker-controlled 32-bit CIS size field survives, is assigned to the INT len (:308) and passed UNCHECKED as the resource count to resource_list_add (:377): 0xFFFFFFF0 -> count = -16; (mode 2) a BAR probing 0x80000000 gives 1 << (ffs(pci_bar)-1) = 1 << 31 (:368), signed-overflow UB manifesting as INT_MIN -> count = -2147483648. A negative count in a child resource-list entry is nonsense for every later rman allocation of that entry. Hardware-gated (physical CardBus card); realistic ceiling is the card's driver failing to attach / resource misbehavior, not privilege escalation.