Missing upper-bound on attacker-controlled stream.len in fwe_as_input causes kernel heap over-read
| Field | Value |
|---|---|
| ID | DF-1849 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H |
| CWE | CWE-125 Out-of-bounds Read |
| File | sys/dev/netif/fwe/if_fwe.c |
| Lines | 560-569 |
| Area | dev/netif (FireWire Ethernet RX) |
| Confidence | likely |
| Discovered | 2026-07-20 |
| Reported | pending |
| Known CVE | none |
| CVE match | variant |
Summary
fwe_as_input() trusts the 16-bit fp->mode.stream.len field from a received
FireWire isochronous stream packet header to set m->m_pkthdr.len, but only
checks the lower bound (>= 16). There is no upper-bound check against the mbuf
cluster capacity (MCLBYTES = 2048). An attacker on the FireWire bus can send a
stream packet whose header claims data_length up to 65535, causing the network
stack to read up to ~63 KB past the 2048-byte mbuf cluster boundary β a kernel
heap over-read that leaks kernel memory to userspace or panics the kernel.
Root cause
In fwe_as_input() the received packet is extracted from the DMA buffer as:
fp = mtod(sxfer->mbuf, struct fw_pkt *); /* line 546 */
The packet's stream.len field is a 16-bit unsigned bitfield
(COMMON_HDR(len,...) β u_int32_t len:16 at firewire.h:130/141) populated
directly by the OHCI controller from the on-wire isochronous packet header β
fully attacker-controlled, never sanitized between OHCI
(fwohci_rbuf_update at fwohci.c:2148-2184 sets chunk->resp from the DMA
status and calls fwe_as_input via ir->hand) and this function.
The validation at lines 560-561 only checks the lower bound:
if (sxfer->resp != 0 || fp->mode.stream.len <
ETHER_ALIGN + sizeof(struct ether_header)) { /* i.e. < 16 */
Lines 567-569 then do:
m->m_data += HDR_LEN + ETHER_ALIGN; /* += 6 bytes */
m->m_len = m->m_pkthdr.len =
fp->mode.stream.len - ETHER_ALIGN;
The mbuf cluster is MCLBYTES = 2048 bytes (xferq->psize set at line 337), and
m_pkthdr.len was MCLBYTES from allocation (line 351/554). After advancing
m_data by 6 bytes, only 2042 bytes of valid data remain. But m_pkthdr.len is
set to stream.len - 2, which can be up to 65533 β far exceeding the cluster.
No check like stream.len > MCLBYTES or stream.len > m->m_pkthdr.len -
HDR_LEN exists.
Threat model & preconditions
- Attacker position: any node on the same IEEE 1394 (FireWire) bus.
FireWire isochronous channels have no authentication β any bus node can
transmit on any channel. Also reachable locally via
/dev/fw*(FW_ASYREQ ioctl with FWASREQSTREAM, fwdev.c:553) since devices are 0660 root:operator (fwdev.c:174). - Privileges gained or impact:
- Kernel heap info leak β leaked bytes are delivered as IP payload to userspace sockets.
- Kernel panic (DoS) if the over-read crosses into an unmapped page.
- Required config or capabilities:
device fweloaded, FireWire controller present, fwe interface up. Default stream_ch=1 (line 84/325). - Reachability: attacker sends a crafted isochronous stream packet
(
tcode=0xa/FWTCODE_STREAM) withdata_length> 2048 in the 4-byte isochronous header, plus a valid Ethernet+IP header in the payload soether_input/ip_inputaccept and process the frame.
Proof of concept
Two vectors.
Vector A β external FireWire bus (no target access needed):
- Connect a Linux attacker machine to the target's FireWire bus.
- Install libraw1394/libavc1394.
- Construct an isochronous stream packet:
- First quadlet =
(sy<<28)|(tcode<<24)|(chtag<<16)|data_length-tcode = 0xa,chtag = 1(default fwe channel) -data_length = 0xFFFF(or any value > 2048) - Follow with a valid Ethernet header (dst=broadcast, src=any, type=0x0800) and
an IP header with valid checksum and
total_lengthmatching the claimeddata_length, soip_inputaccepts it and delivers "payload" (kernel heap beyond the cluster) to a socket the attacker reads. - Send the packet. The target's
fwe_as_inputcreates an mbuf withm_pkthdr.lenup to 65533 but only ~2042 bytes of real data.ether_inputβip_inputreads past the cluster.
Vector B β local, operator group:
Open /dev/fw0, issue FW_ASYREQ ioctl with req.type=FWASREQSTREAM,
pkt.mode.stream.tcode=0xa, pkt.mode.stream.chtag=1,
pkt.mode.stream.len=0xFFFF, and a crafted payload β same over-read result.
Build & run
cc -o fw_poc fw_poc.c -lraw1394 # Vector A # OR cc -o fw_poc fw_poc.c # Vector B (uses ioctl)
Expected output
# On target: Fatal trap 12: page fault while in kernel mode ... ether_input+0x.. / ip_input+0x.. at 0x.. # OR silent heap leak to userspace socket buffer
Impact
Medium-severity kernel heap over-read reachable from any node on the FireWire
bus (unauthenticated isochronous channel) or locally via /dev/fw* (operator
group). The over-read can disclose kernel memory (KASLR bypass, credential
disclosure) or panic the kernel (DoS).
Recommended fix
Add an upper-bound check on fp->mode.stream.len before using it to set the
mbuf length. The received data must fit within the mbuf cluster after the
m_data advancement.
--- a/sys/dev/netif/fwe/if_fwe.c
+++ b/sys/dev/netif/fwe/if_fwe.c
@@ -557,8 +557,10 @@ fwe_as_input(struct fw_xferq *xferq)
}
if (sxfer->resp != 0 || fp->mode.stream.len <
- ETHER_ALIGN + sizeof(struct ether_header)) {
+ ETHER_ALIGN + sizeof(struct ether_header) ||
+ fp->mode.stream.len > m->m_pkthdr.len - HDR_LEN) {
m_freem(m);
IFNET_STAT_INC(ifp, ierrors, 1);
continue;
At this point m->m_pkthdr.len == MCLBYTES (2048) from allocation, so the check
rejects any stream.len > 2044. After m_data += 6, the new length
(stream.len - 2 β€ 2042) fits within the remaining cluster space. An equivalent
alternative: compare stream.len directly against MCLBYTES:
fp->mode.stream.len > MCLBYTES - HDR_LEN.
References
- OHCI RX buffer update path: fwohci.c:2148-2184.
/dev/fw*stream ioctl path: fwdev.c:553.stream.lenbitfield: firewire.h:130/141.
Timeline
- 2026-07-20 Discovered during automated audit.
- 2026-07-20 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1849 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able unified diff; validated as part of combined 41-finding kernel build (rc=0, -Werror clean) | 553 B | view raw |
| VERDICT.md | verdict | source-only confirmation + HW/module gating explanation | 1.5 KB | β raw |
DF-1849 Verification
Verdict
SOURCE-CONFIRMED, INCONCLUSIVE-RUNTIME (HW/module gated).
The cited defect exists in the audited source at sys/dev/netif/fwe/if_fwe.c:560-569. Reproduction
on the running guest is not possible because the affected code path is
gated behind hardware that is not present in the audit QEMU/KVM guest
(no AMD/i915 GPU, no LSI MegaRAID, no MMC/SDHCI controller, no FireWire, no
ATAPI floppy, etc.) and/or lives in a kernel module that is not loaded on the
GENERIC-running guest.
Mechanism (source-only confirmation)
fwe (Ethernet over FireWire) IS in GENERIC but no FireWire HW in guest. Source: fwe_as_input at L546 extracts fp=mtod(...). stream.len is a u16 bitfield populated directly by the OHCI controller from the on-wire isochronous packet header (attacker-controlled). The check at L560 only rejects
Recommended fix
Cap fp->mode.stream.len against m->m_ext.ext_size before computing m->m_len.
The full git apply-able diff lives in fix.diff in this folder; it was
applied as part of a single combined 41-finding kernel build that compiled
cleanly (rc=0, -Werror clean) β see ../fix_build_summary.txt.
Build validation
git apply --checkon this fix.diff: OK- Combined kernel build (
X86_64_GENERIC, INVARIANTS ON) with all 41 findings' fix.diffs applied: rc=0, no warnings, no errors. - The patched kernel was not booted/run because the affected code path requires hardware that the audit guest does not have.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- f
- w
- e
- /
- i
- f
- _
- f
- w
- e
- .
- c
- :
- 5
- 6
- 0
- -
- 5
- 6
- 9
Detail
Exploit chain
none β non-corruption classes (info leak / DoS / div0 / logic) or HW/module gated. No memory-corruption primitive reachable from userspace on this guest.
Evidence (decisive lines)
Source-only confirmation. Combined kernel build with all 41 fix.diffs applied: === NK_DONE rc=0 === at Wed Jul 22 18:05:21 UTC 2026 (no errors, no warnings). See findings/poc/fix_build_summary.txt.
PoC changes
Authored findings/poc/DF-1849/fix.diff (minimal targeted guard). VERDICT.md and manifest.json written. fix.diff validated by combined build.
Verified recommended fix
Cap fp->mode.stream.len against m->m_ext.ext_size before computing m->m_len. Full git-apply-able diff in findings/poc/DF-1849/fix.diff; validated as part of combined 41-finding kernel build (rc=0).
Verdict
SOURCE-CONFIRMED, INCONCLUSIVE-RUNTIME. The cited defect exists at sys/dev/netif/fwe/if_fwe.c:560-569. fwe (Ethernet over FireWire) IS in GENERIC but no FireWire HW in guest. fwe_as_input L546 extracts fp=mtod(...). stream.len is a u16 bitfield populated by OHCI controller from on-wire isochronous packet (attacker-controlled). Validation L560-564 only rejects too-small; large stream.len drives m->m_len past m_ext.ext_size (heap OOB write). HW gated.
No comments yet.