Unauthenticated remote heap overflow in xe RX: missing upper-bound check on frame length before copying into 2KB mbuf cluster
Summary
xe_intr at if_xe.c:752: len=XE_INW(XE_RBC)-ETHER_CRC_LEN. No mask vs XE_RBC_BYTE_COUNT(0x1FFF). No check vs MCLBYTES(2048). len==0 guard misses unsigned wrap (RBC<4 -> len=0xFFFC..0xFFFF). Silicon accepts long packets up to 8184B. bus_space_read_multi_2 copies up to 8187 (or ~64KB wrap) bytes into 2046-byte mbuf cluster window. Remote on same L2 segment. Sibling pattern: every other DFly NIC driver checks MCLBYTES. Fix: mask RBC, check len<=MCLBYTES-2-1, check pktlen>=ETHER_CRC_LEN.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1410 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | reproduces the buggy len arithmetic + (len+1)>>1 word count | 4.1 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -o harness harness.c | 65 B | view raw |
| run.sh | run-script | ./harness | 89 B | view raw |
| build.log | build-log | in-guest build, BUILD_EXIT=0 | 13 B | view raw |
| run.log | run-log | decisive run; 4/7 cases overflow 6KiB..63KiB | 999 B | view raw |
| env.txt | environment | uname + guest PCI inventory (no Xircom) | 543 B | view raw |
| fix.diff | suggested-fix | mask XE_RBC, range-check vs MCLBYTES before cluster attach | 866 B | view raw |
| fix_build.log | fix-build-log | patched nativekernel, rc=0 | 5.6 MB | β download |
| VERDICT.md | verdict | full narrative | 4.0 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1410 β xe(4) RX length missing upper bound (heap overflow)
Verdict
REPRODUCED (source-level harness). The bug is real; impact ceiling is
remote (unauthenticated, same-L2-segment) heap overflow of a 2 KiB mbuf
cluster, with overflow sizes from ~6 KiB (long packet) up to ~63 KiB (RBC<4
unsigned wrap). The kernel code path cannot be exercised on this guest
because QEMU does not emulate any PCMCIA bridge or the Xircom CE hardware β
xe0 does not exist in pciconf -lv. The fix.diff applies cleanly and
nativekernel succeeds (rc=0); no run-time exercise is possible because the
device is absent.
Mechanism (sys/dev/netif/xe/if_xe.c)
- Line 750:
u_int16_t len; - Line 752:
len = XE_INW(XE_RBC) - ETHER_CRC_LEN;β no mask againstXE_RBC_BYTE_COUNT(if_xereg.h:269, 0x1FFF) so the 3 flag bits in the high half contaminate the count; no upper-bound check vsMCLBYTES(2048); no lower-bound check thatRBC >= ETHER_CRC_LEN, so an RBC value of 0..3 wraps tolen = 0xFFFC..0xFFFF. - Lines 775-782:
MCLGETattaches a 2 KiB cluster; line 784m_data += 2reduces the usable window toMCLBYTES - 2. - Lines 822/826:
bus_space_read_multi_2(bst, bsh, XE_EDP, ehp, (len+1)>>1)copies2*ceil(len/2)bytes from the card into that cluster window β for any oversized or wrappedlen, this writes thousands of bytes past the cluster into the adjacent kernel heap.
Sibling drivers (dc, fxp, rl, etc.) all mask their RX-length register
and bound against MCLBYTES; xe is the outlier.
Harness proof (harness.c)
Uses the genuine register layout (XE_RBC_BYTE_COUNT = 0x1fff), the genuine
len = RBC - ETHER_CRC_LEN arithmetic, and the genuine
(len+1)>>1 word count. 4 of 7 representative inputs (silicon-long 8184 B
packet, plus RBC=0/1/3 wraps) produce overflows of 6 KiB..63 KiB. The fixed
variant (mask + range-check) accepts the legitimate packets and rejects the
oversized/wrapped ones:
case RBC bug_len oob_B fix_len oob_B silicon long 8184B packet 8184 8180 6134 -1 0 RBC=0 (wrap to 0xFFFC) 0 65532 63486 0 0 RBC=1 (wrap to 0xFFFD) 1 65533 63488 0 0 RBC=3 (wrap to 0xFFFF) 3 65535 63490 0 0 Buggy driver: 4/7 cases overflow the 2KiB mbuf cluster
Exploit-chain note
This is a remote unauthenticated heap-overflow primitive on real hardware. QEMU has no Xircom PCMCIA device, so the kernel-side chain cannot be exercised here. On a deployed Xircom CE2 system, every received long packet (8 KiB) or underflowed RBC corrupts adjacent heap deterministically; this is a credible remote heap-grooming β kernel-code-exec primitive in a real deployment. Documented as primitive characterization; impact ceiling is remote heap corruption.
PoC changes
- Original PoC folder had no source (
build.sh/run.shwere placeholders). - Added
harness.cplus repro scripts, env, build/run logs, fix.diff, VERDICT.md, manifest.json.
Fix
fix.diff masks XE_RBC against XE_RBC_BYTE_COUNT, requires
len >= ETHER_CRC_LEN, and rejects len - ETHER_CRC_LEN > MCLBYTES - 2 - 1
before attaching the cluster. This matches the pattern every sibling NIC
driver uses. Matches the finding markdown proposal.
Fix-validation
patch -p1 --forward succeeds (hunk #1 at line 749). nativekernel
completes with rc=0 (saved as fix_build.log). The kernel-side before/after
cannot be exercised because the device is absent β fix_status:
"not_testable". The diff is verified to apply and compile, and the changed
logic is traced to close the cited overflow.
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
not_testablenot_testable because the Xircom CE PCMCIA NIC is absent from the audit guest; validated that fix.diff applies cleanly (hunk #1 at line 749) and the single-fix nativekernel compiles rc=0 (fix_build.log). Traced the changed logic to mask + range-check before the cluster attach.
baseline (harness): Buggy driver: 4/7 cases overflow the 2KiB mbuf cluster patched kernel build: === NK_DONE rc=0 ===
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- x
- e
- /
- i
- f
- _
- x
- e
- .
- c
- :
- 7
- 5
- 0
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- x
- e
- /
- i
- f
- _
- x
- e
- .
- c
- :
- 7
- 5
- 2
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- x
- e
- /
- i
- f
- _
- x
- e
- .
- c
- :
- 8
- 2
- 2
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- x
- e
- /
- i
- f
- _
- x
- e
- .
- c
- :
- 8
- 2
- 6
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- x
- e
- /
- i
- f
- _
- x
- e
- r
- e
- g
- .
- h
- :
- 2
- 6
- 9
Detail
Exploit chain
none (HW-gated): Xircom CE2 is PCMCIA and QEMU has no PCMCIA bridge, so the in-kernel path cannot be exercised. Primitive characterized via harness: remote unauthenticated same-L2 heap overflow of a 2 KiB mbuf cluster with overflow sizes 6 KiB..63 KiB. Realistic ceiling on a deployed Xircom NIC: remote heap-grooming -> kernel-code-exec.
Evidence (decisive lines)
case RBC bug_len oob_B fix_len oob_B silicon long 8184B packet 8184 8180 6134 -1 0 RBC=0 (wrap to 0xFFFC) 0 65532 63486 0 0 RBC=1 (wrap to 0xFFFD) 1 65533 63488 0 0 RBC=3 (wrap to 0xFFFF) 3 65535 63490 0 0 Buggy driver: 4/7 cases overflow the 2KiB mbuf cluster
PoC changes
Original folder had no source (placeholder build.sh/run.sh). Added harness.c reproducing the genuine len arithmetic and word-count computation, plus repro scripts, env, full logs, fix.diff, VERDICT.md, manifest.json.
Verified recommended fix
fix.diff masks XE_RBC against XE_RBC_BYTE_COUNT and rejects len < ETHER_CRC_LEN or len-ETHER_CRC_LEN > MCLBYTES-2-1 before attaching the cluster, matching the pattern every sibling NIC driver uses. Matches finding markdown proposal.
Verdict
REPRODUCED at the source-logic level. if_xe.c:752 len = XE_INW(XE_RBC) - ETHER_CRC_LEN has no mask against XE_RBC_BYTE_COUNT (0x1fff, if_xereg.h:269) and no upper-bound vs MCLBYTES; line 826 bus_space_read_multi_2 copies (len+1)>>1 16-bit words into a 2048-byte mbuf cluster window. For RBC<4 the unsigned wrap yields len=0xFFFC..0xFFFF (63486..63490 byte overflow), and silicon-accepted 8 KiB packets yield len=8180 (6134 byte overflow). Harness confirms 4/7 representative inputs overflow; fixed variant rejects them all. No Xircom CE PCMCIA hardware on the guest so the kernel path cannot be triggered here; harness proof only.
No comments yet.