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

cardbus_cis: decode_tuple_funce validates the LAN-NID length only against sizeof(nid), never against the tuple length β€” the card's MAC (PCI_IVAR_ETHADDR) is fabricated from stale bytes of previously parsed tuples; fepresent |= 1<<type is a UB shift for type>=32

Summary

The check only bounds tupledata[1] by sizeof(nid); a 2-byte FUNCE tuple claiming 6 NID bytes copies stale bytes of PREVIOUS tuples from the shared buffer into the ETHADDR ivar (demonstrated). No kernel-memory disclosure (contents always card-supplied or zero) - hardening/correctness. Separately 1<<type with type up to 255 is UB. Require-length-match + guard-shift fix (validated).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4691 Β· 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 β€” 722 B view raw
VERDICT.md β€” 2.1 KB ↓ raw
manifest.json β€” 760 B view raw
verdict.json β€” 3.1 KB view raw
VERDICT.md
↓ download raw

DF-4691 -- verdict narrative

Claim

decode_tuple_funce validates the LAN-NID copy length only against sizeof(nid) (6), never against the actual tuple length (cardbus_cis.c:278-283): a 2-byte FUNCE tuple {LAN_NID, 6} makes bcopy read tupledata[2..7] -- past the tuple's own payload -- and those bytes become dinfo->funce.lan.nid, handed out as the card's Ethernet MAC (PCI_IVAR_ETHADDR, cardbus.c:327-333). tupledata is the shared MAXTUPLESIZE(0x400) buffer, bzero'd once (cardbus_cis.c:656) then overwritten per tuple, so the "MAC" is fabricated from STALE bytes of previously parsed tuples (or zeros). Separately, dinfo->fepresent |= 1<<type (:286) with type = tupledata[0] up to 255 is a UB shift.

Reproduced?

YES, decoder-level (verbatim extraction on the DragonFly guest): after seeding the buffer with 0xE0.. (a previous tuple's payload), the 2-byte nid tuple yields nid = e2:e3:e4:e5:e6:e7 -- the read past the tuple is real and the stale bytes land in the ETHADDR ivar. fepresent after a type=200 tuple is 0x110 (1<<8): x86 silently masks the shift count (UB); on other ISAs/recompiles the value differs.

Impact ceiling

No kernel-memory disclosure (all bytes in tupledata are card-supplied or zero). The attacker already controls their own card, so a fabricated MAC is not a boundary crossing; the bug is a correctness/hygiene defect plus C UB. Filed as Info (hardening). Hardware-gated: requires CardBus bridge + card.

Fix validation (fix.diff = fix4691.diff)

Baseline: nid = stale bytes (above). Patched (require tupledata[1] == len-2 and guard type < 32): the mismatched tuple is ignored -- nid stays 00:00:00:00:00:00 and fepresent stays 0x0. (The harness prints "unexpected nid content" on the FIXED build because its check text assumes the vulnerable outcome.) 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 nid = stale 0xE2.. bytes; with fix4691.diff the mismatched tuple is ignored (nid stays zeroed) and 1<<200 no longer sets any bit (fepresent 0x0).

run.log: 'nid (PCI_IVAR_ETHADDR, cardbus.c:333) = e2:e3:e4:e5:e6:e7' + 'REPRODUCED: nid = stale bytes ... read past the 2-byte tuple at :282' + 'fepresent after 1<<200 (UB ...) = 0x110'; fix_run.log: 'PATCHED mode 4 ... nid = 00:00:00:00:00:00 ... fepresent = 0x0'.
↓ fix.diffper-fix-DF-4691

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: 'nid (PCI_IVAR_ETHADDR, cardbus.c:333) = e2:e3:e4:e5:e6:e7' + 'REPRODUCED: nid = stale bytes ... read past the 2-byte tuple at :282' + 'fepresent after 1<<200 (UB ...) = 0x110'; fix_run.log: 'PATCHED mode 4 ... nid = 00:00:00:00:00:00 ... fepresent = 0x0'.

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

Require the NID length to equal the tuple payload length (tupledata[1] == len-2) and guard the fepresent shift with type < 32.

Verdict

REPRODUCED at decoder level on the DragonFly guest (verbatim-extracted decode_tuple_funce, cardbus_cis.c:257-290). The LAN-NID copy length is validated only against sizeof(nid)=6 (:278), never against the tuple's actual length: a 2-byte FUNCE tuple {TPLFE_TYPE_LAN_NID, 6} passes and bcopy(:282-283) reads tupledata[2..7] -- past the tuple payload -- into dinfo->funce.lan.nid, the card's Ethernet MAC ivar (PCI_IVAR_ETHADDR, cardbus.c:327-333). tupledata is the shared 0x400 parse buffer (bzero once at :656, then overwritten per tuple): demonstrated nid = e2:e3:e4:e5:e6:e7 fabricated from a PREVIOUS tuple's stale bytes. No kernel-memory disclosure (contents always card-supplied/zero) -- Info/hardening. Separately fepresent |= 1<<type (:286) with type<=255 is UB (x86 masks to 1<<8: 0x110 observed). Hardware-gated (physical CardBus card).