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

nfe_alloc_{rx,tx}_ring error paths destroy data[i].map in a loop over j β€” dmamap leak + tag map_count underflow on attach failure

Summary

Both ring-allocation failure paths run 'for (j...) bus_dmamap_destroy(ring->data_tag, ring->data[i].map);' - destroying the FAILING slot's map (NULL, since create sets *mapp=NULL on error) i times while the i successfully-created maps leak; dmat->map_count underflows by i because destroy decrements unconditionally. Verified NOT a double-free (busdma read). Index-typo fix [i]->[j].

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4052 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.9 KB ↓ raw
VERDICT.md β€” 1.6 KB ↓ raw
fix.diff β€” 633 B view raw
manifest.json β€” 628 B view raw
verdict.json β€” 2.2 KB view raw

DF-4052 β€” nfe_alloc_{rx,tx}_ring error paths destroy data[i].map in a loop over j (maps 0..i-1 leak; tag map_count underflows)

What

sys/dev/netif/nfe/if_nfe.c:

static int
nfe_alloc_rx_ring(...)                    /* :1642 */
{
    ...
    for (i = 0; i < sc->sc_rx_ring_count; i++) {
        error = bus_dmamap_create(ring->data_tag, BUS_DMA_WAITOK,
                      &ring->data[i].map);
        if (error) {
            ...
            goto fail;
        }
    }
    return 0;
fail:
    for (j = 0; j < i; ++j)
        bus_dmamap_destroy(ring->data_tag, ring->data[i].map);   /* :1726 - [i], not [j]! */
    bus_dmamap_destroy(ring->data_tag, ring->data_tmpmap);
    ...

The cleanup loop iterates j but destroys ring->data[i].map β€” the map at the failing index, which bus_dmamap_create has already set to NULL on error (busdma_machdep.c:430: *mapp = NULL before return error). Each iteration is therefore a NULL no-op while the i successfully-created maps (data[0].map .. data[i-1].map) are leaked; additionally bus_dmamap_destroy decrements dmat->map_count unconditionally (busdma_machdep.c:440-448), so the tag's map count underflows by i and the later bus_dma_tag_destroy runs against a bogus count.

The identical typo exists in nfe_alloc_tx_ring (:1987-1989).

Reachability

Requires bus_dmamap_create (a kmalloc(M_DEVBUF)) to fail mid-ring β€” i.e. kernel-memory exhaustion during attach of an nfe(4) device β€” after which attach aborts with the maps leaked forever (per attempt). Low severity, OOM-gated, certain.

Fix

-       bus_dmamap_destroy(ring->data_tag, ring->data[i].map);
+       bus_dmamap_destroy(ring->data_tag, ring->data[j].map);

in both functions (fix.diff).

Verification

Source-certain (double-checked that a NULL map destroy is a no-op and that bus_dmamap_create NULLs *mapp on failure, so this is a leak, not a double-free). Not runtime-verified: needs nfe hardware + induced kmalloc failure. Status: untested.

VERDICT.md
↓ download raw

DF-4052 VERDICT β€” [i]-vs-[j] typo in ring-allocation error paths

Status: untested (source-certain; Low severity; OOM + hardware gated)

  • if_nfe.c:1725-1726 and :1988-1989: cleanup loops for (j = 0; j < i; ++j) bus_dmamap_destroy(ring->data_tag, ring->data[i].map); destroy the failing slot's map ([i]) i times instead of the i successfully-created maps ([0..i-1]).
  • Verified semantics that bound the impact:
  • bus_dmamap_create writes *mapp = NULL before returning error (sys/platform/pc64/x86_64/busdma_machdep.c:425-433) β†’ destroying data[i].map is a NULL no-op β†’ leak, not double-free (an early audit hypothesis, refuted by reading busdma).
  • bus_dmamap_destroy still does dmat->map_count-- for NULL maps (busdma_machdep.c:440-448) β†’ tag map-count underflow by i.
  • Trigger: kmalloc failure inside bus_dmamap_create mid-ring during nfe_attach (memory-pressure-gated) β†’ attach fails, nfe_detach runs, and i dmamaps (~64-88 bytes each) plus the bogus tag count persist. Repeatable per attach attempt.
  • No memory-safety consequence demonstrated; classic error-path resource leak. Low severity, confidence certain, not runtime-verifiable on this guest (no nForce NIC; inducing the OOM inside attach is not possible without the device).

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 no→ patch + rebuild →patched clean

Hardware-gated; one-character index fix, source-level only.

findings/poc/DF-4052/fix.diff
↓ fix.diffper-fix-DF-4052

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md/VERDICT.md with both loops quoted and the busdma semantics (NULL no-op destroy, unconditional map_count--) that bound impact to leak+counter-underflow

Verified recommended fix

Index the destroy loop with j, not i, in both nfe_alloc_rx_ring and nfe_alloc_tx_ring; see fix.diff

Verdict

Source-certain error-path typo: nfe_alloc_rx_ring (if_nfe.c:1725-1726) and nfe_alloc_tx_ring (:1988-1989) run 'for (j = 0; j < i; ++j) bus_dmamap_destroy(ring->data_tag, ring->data[i].map);' - destroying the failing slot's (NULL) map i times instead of the i successfully-created maps 0..i-1, which leak; dmat->map_count underflows by i because bus_dmamap_destroy decrements unconditionally (busdma_machdep.c:440-448). Verified NOT a double-free because bus_dmamap_create sets *mapp = NULL before returning error (busdma_machdep.c:425-433). Trigger is a bus_dmamap_create kmalloc failure mid-ring during attach (kernel memory pressure) on nfe(4) hardware - not reproducible on this guest (no nForce NIC), status untested. Low severity resource leak; classic pass-2 error-path class; one-character fix ([i] -> [j]) in both functions.