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