oce_tx returns 0 (success) when oce_insert_vlan_tag fails on !M_WRITABLE mbufs: the dequeued mbuf is leaked and the packet silently dropped while oce_start_locked believes it was sent
Summary
When the ASIC-stall verify path's oce_insert_vlan_tag returns NULL (!M_WRITABLE), oce_tx does 'return 0'. The caller treats rc==0 as delivered: BPF-taps and dequeues the next packet - the still-owned mbuf is never freed (leak) and never transmitted. Gated on BE3 rev-A1 + QnQ/UMC + IPv6 ext-hdr + read-only mbuf (very narrow), but the error-path logic is plainly wrong; ceiling is mbuf exhaustion via ordinary IPv6 traffic on affected profiles. ENOBUFS-return fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4082 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.0 KB | β raw | |
| VERDICT.md | β | 1.6 KB | β raw | |
| verdict.json | β | 1.7 KB | view raw | |
| manifest.json | β | 602 B | view raw |
DF-4082 β oce_tx returns 0 (success) when oce_insert_vlan_tag fails: mbuf
leaked and packet silently dropped while the caller believes it was sent
File: sys/dev/netif/oce/oce_if.c (pass 2) Class: error-path logic bug (memory leak + silent drop)
Root cause
oce_tx() (sys/dev/netif/oce/oce_if.c):
if (oce_tx_asic_stall_verify(sc, m)) { /* 833 */
m = oce_insert_vlan_tag(sc, m, &complete);/* 834 */
if (!m) {
device_printf(sc->dev, "Insertion unsuccessful\n");
return 0; /* 837 β SUCCESS! */
}
}
oce_insert_vlan_tag (oce_if.c:2202-2236) returns NULL exactly when
!M_WRITABLE(m) (2207-2208) β i.e. read-only external mbufs (M_EXT with a
non-standard free/readonly mapping, e.g. sendfile-style or otherwise
EXT_RDONLY chains).
On that path:
- *mpp is left untouched (still the original mbuf),
- the WQ/pckts state is untouched,
- the function returns 0 = success.
Caller oce_start_locked (oce_if.c:1129-1147): rc == 0 β BPF tap (1144-1145) β loop continues β the dequeued mbuf is never freed and never transmitted. Per-packet mbuf(+cluster) leak plus silent loss, with a false success return.
Gating (narrow): needs oce_tx_asic_stall_verify == TRUE, i.e. BE3 rev A1 (asic_revision < 2, oce_if.c:2193-2200) AND QnQ/UMC mode (pvid/qnqid set, oce_if.h:1162-1163) AND an IPv6 packet with extension headers (oce_check_ipv6_ext_hdr, oce_if.c:2167-2191) AND a read-only mbuf. Extremely narrow hardware profile β hence Low severity β but the error-path logic is plainly wrong.
Fix
Return an error and let the caller free/requeue, or free locally:
if (!m) {
device_printf(sc->dev, "Insertion unsuccessful\n");
- return 0;
+ return ENOBUFS; /* caller frees *mpp */
}
(ENXIO/ENOMEM both work with oce_start_locked's handling at 1135-1142 β with m != NULL it prepends back to the ifq instead of leaking.)
Verification status
UNTESTED β hardware-gated (BE3-A1 + UMC/QnQ profile cannot be present in QEMU).
DF-4082 VERDICT β oce_tx vlan-insert failure returns success
Status: UNTESTED (hardware-gated; logic is source-certain)
Trace:
- oce_if.c:833-840: on oce_tx_asic_stall_verify() TRUE, oce_insert_vlan_tag()
failure (NULL) prints and
return 0;. - oce_if.c:2207-2208: NULL return iff
!M_WRITABLE(m). - oce_if.c:1134-1145: oce_start_locked treats rc==0 as delivered: BPF tap, next dequeue. The mbuf (still valid, still owned by the ifq-dequeued chain) is never freed β leak β and the packet never reaches the WQ.
Trigger conditions stack: is_be3_a1 (BE3, asic_revision<2) && IS_QNQ_OR_UMC && ipv6 ext-hdr packet && read-only mbuf chain. All hardware/config gated except the read-only-mbuf input, which the socket layer can produce. No QEMU emulation of the NIC β untestable here; severity Low on trigger narrowness (Info-adjacent), but the code defect is unambiguous and the fix is one line.
Impact ceiling: unbounded mbuf/cluster leak (memory exhaustion DoS) and silent packet loss on the affected profile, driven by ordinary IPv6 traffic.
Fix
Return an error (ENOBUFS) instead of 0 so oce_start_locked's error path (ifq_prepend at 1139) takes the mbuf back.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableFix validation not possible: no OneConnect hardware/emulation in QEMU.
Guest pciconf -l: no 0x19a2/0x10df devices.
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md, VERDICT.md. Key lines: oce_if.c:833-840 (return 0 on failure), 2207-2208 (NULL path), 1134-1145 (caller trusts rc==0).
PoC changes
No runnable PoC: hardware-gated (BE3-A1/UMC profile).
Verified recommended fix
Return ENOBUFS (not 0) when oce_insert_vlan_tag fails so the caller requeues/frees the mbuf.
Verdict
oce_tx returns 0 (success) when oce_insert_vlan_tag fails on !M_WRITABLE mbufs (oce_if.c:833-840, NULL source at 2207-2208): oce_start_locked (1129-1147) then taps BPF and dequeues the next packet, so the mbuf is leaked and the packet silently dropped under a false success. Requires the BE3-A1 + QnQ/UMC + IPv6-ext-hdr + read-only-mbuf profile. Untested: hardware-gated (no oce NIC in QEMU); one-line fix.
No comments yet.