β¬’ 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 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
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 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_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.