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

txp(4) attach error path leaks the 256 rb_sd swdesc kmallocs (and ifmedia) when txp_alloc_rings() fails after allocating them

Summary

txp_alloc_rings kmallocs 256 persistent swdescs whose only references live inside the DMA-shared ldata block; on later failure the attach fail-path frees ldata but not the swdescs (4096 bytes/failed attach); the post-ifmedia_init bus_setup_intr failure path also skips ifmedia_removeall. Bounded leak on a hardware-gated error path. Free-under-guard + ifmedia_removeall fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4234 Β· 8 files
FileTypeDescriptionSize
trigger.c β€” 169 B view raw
build.log β€” 77 B view raw
run.log β€” 1.2 KB view raw
env.txt β€” 688 B view raw
fix.diff β€” 1.2 KB view raw
VERDICT.md β€” 2.0 KB ↓ raw
README.md β€” 1.6 KB ↓ raw
verdict.json β€” 2.3 KB view raw

DF-4234 β€” txp(4) attach error path leaks the 256 rb_sd swdesc allocations (and ifmedia)

File: sys/dev/netif/txp/if_txp.c (pass 2) Severity: Low (kernel memory leak, no attacker-reachable trigger β€” needs a card whose firmware boots far enough to allocate rings but then fails the boot-record handshake)

The bug

txp_alloc_rings() (if_txp.c:948-956) kmallocs one struct txp_swdesc per receive-buffer slot (256 Γ— 16 B) and stores the pointers only inside the DMA-shared ring (sc_rxbufs[i].rb_sd, which is &sc->sc_ldata-> txp_rxbufs). If the function later fails (fw not running / not waiting for boot, if_txp.c:972-991), txp_attach() does goto fail β†’ txp_release_resources() (if_txp.c:358-374) frees irq, res and the whole sc_ldata block β€” but not the 256 kmalloc'd swdescs, whose only references lived inside sc_ldata. ~4 KB leaked per failed attach; repeated reprobe cycles (devctl/kldload loops on a half-broken card) accumulate.

Secondary asymmetry: after ifmedia_init() at if_txp.c:284, the bus_setup_intr failure path (if_txp.c:315-322) calls ether_ifdetach() but never ifmedia_removeall() (compare txp_detach() :346).

No PoC (Low, hardware-gated error path β€” trigger needs a 3c990 that fails boot-record registration). Status untested per convention; the guest probe run.log (shared with DF-4231) shows the driver is in the kernel and no 3Com device exists.

Fix

Free the swdescs in txp_release_resources() before freeing sc_ldata (guarded by sc_rxbufs != NULL), and ifmedia_removeall() on the late attach-failure path.

VERDICT.md
↓ download raw

DF-4234 VERDICT β€” attach error-path swdesc leak

Verdict: source-certain Low memory leak on a hardware-gated error path; untested (no PoC β€” Low severity, hardware precondition).

Trace

txp_attach()                    if_txp.c:202
  :241   txp_chip_init()        ok
  :248   txp_download_fw()      ok
  :255   sc->sc_ldata = contigmalloc(...)         ok
  :258   txp_alloc_rings(sc)
           :948-956  for (i = 0; i < RXBUF_ENTRIES; i++)
                       sc->sc_rxbufs[i].rb_sd = kmalloc(sizeof(struct txp_swdesc),
                                                        M_DEVBUF, M_WAITOK);
                     // pointers stored ONLY inside sc_ldata
           :972-991  ... READ_REG(TXP_A2H_0) != STAT_WAITING_FOR_BOOT /
                       != STAT_RUNNING  β†’  return(ENXIO)
  :259   error = ENXIO; goto fail;
fail:
  :327   txp_release_resources(dev);
           :370-371  contigfree(sc->sc_ldata, ...)   // rb_sd pointers die here
           // the 256 kmalloc'd swdescs are leaked (256 * 16 = 4096 bytes)

txp_detach() frees them (if_txp.c:349-350) but is only reached for successfully-attached devices. device_is_attached() gating elsewhere confirms attach-failure never routes through detach.

Fix in fix.diff: free rb_sd in txp_release_resources() under a sc_rxbufs != NULL guard (it points into sc_ldata, so check before the contigfree), plus ifmedia_removeall() for the post-ifmedia_init failure path at :315-322 (mirroring txp_detach():346).

Not runtime-verifiable: requires a 3c990 that passes chip-init + fw download but fails the boot-record handshake (run.log: no such device on the guest). Severity Low: no unprivileged trigger, bounded leak per event.

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

Low-severity leak fix; not runtime-validable (hardware-gated error path). Diff is build-safe and dedups the detach free.

fix.diff
↓ fix.diffper-fix-DF-4234

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (error-path trace), fix.diff (leak fix incl. detach-loop dedup to avoid double-free), run.log (driver in kernel, NO_3COM_PCI_DEVICE)

PoC changes

n/a -- Low finding, PoC skipped per contract (verdict status 'untested').

Verified recommended fix

Free rb_sd in txp_release_resources before contigfree of sc_ldata; ifmedia_removeall on the late attach-failure path (see fix.diff).

Verdict

Source-certain Low: txp_alloc_rings() kmallocs 256 persistent swdescs whose only references live inside the DMA-shared sc_ldata block (if_txp.c:948-956); when the function later fails (fw boot-record handshake, :972-991), txp_attach's fail path runs txp_release_resources() (:326-328 -> :358-374) which frees irq/res/ldata but not the swdescs -> 4096 bytes leaked per failed attach on a half-broken card; secondary: the post-ifmedia_init bus_setup_intr failure path (:315-322) skips ifmedia_removeall(). Untested (Low, hardware-gated error path; no PoC per contract -- guest has no 3c990, run.log shared with DF-4231).