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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1410 Β· 11 files
FileTypeDescriptionSize
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 41 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 3.6 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
VERDICT.md verdict full narrative
↓ download 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)

  1. Line 750: u_int16_t len;
  2. Line 752: len = XE_INW(XE_RBC) - ETHER_CRC_LEN; β€” no mask against XE_RBC_BYTE_COUNT (if_xereg.h:269, 0x1FFF) so the 3 flag bits in the high half contaminate the count; no upper-bound check vs MCLBYTES (2048); no lower-bound check that RBC >= ETHER_CRC_LEN, so an RBC value of 0..3 wraps to len = 0xFFFC..0xFFFF.
  3. Lines 775-782: MCLGET attaches a 2 KiB cluster; line 784 m_data += 2 reduces the usable window to MCLBYTES - 2.
  4. Lines 822/826: bus_space_read_multi_2(bst, bsh, XE_EDP, ehp, (len+1)>>1) copies 2*ceil(len/2) bytes from the card into that cluster window β€” for any oversized or wrapped len, 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.sh were placeholders).
  • Added harness.c plus 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 verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

not_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 ===
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0 master+df1410-fix (single-fix kernel built, rc=0)

Confirmed kernel references

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.