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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4082 Β· 4 files
FileTypeDescriptionSize
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).

VERDICT.md
↓ download raw

DF-4082 VERDICT β€” oce_tx vlan-insert failure returns success

Status: UNTESTED (hardware-gated; logic is source-certain)

Trace:

  1. oce_if.c:833-840: on oce_tx_asic_stall_verify() TRUE, oce_insert_vlan_tag() failure (NULL) prints and return 0;.
  2. oce_if.c:2207-2208: NULL return iff !M_WRITABLE(m).
  3. 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_testable
baseline no→ patch + rebuild →patched clean

Fix validation not possible: no OneConnect hardware/emulation in QEMU.

Guest pciconf -l: no 0x19a2/0x10df devices.
per-fix-DF-4082

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.