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

Heap memory corruption: kfree() on mbuf data pointer in ng_device_rcvdata

Summary

ng_device_rcvdata (:363-377): line 363 buffer=kmalloc(sizeof*m_len,M_DEVBUF) allocates scratch. Line 369 buffer=mtod(m,char*) OVERWRITES kmalloc ptr with mbuf data area. Line 372 memcpy uses buffer (=mtod) OK. Line 377 kfree(buffer,M_DEVBUF) frees MBUF DATA POINTER not the kmalloc. Original kmalloc LEAKED forever. kfree on mbuf-internal pointer (m_pktdat or cluster) with wrong M_DEVBUF type corrupts malloc/UMA allocator. Also no m_freem(m) -> mbuf leaked. Caller ng_send_data expects rcvdata to consume/free m. Repeated triggering = heap corruption panic or groomable for priv-esc.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0660 Β· 7 files
FileTypeDescriptionSize
fix.diff suggested-fix drop bogus kmalloc/kfree; use mtod directly; m_freem(m) 585 B view raw
VERDICT.md verdict source trace + dead-code reachability 4.1 KB ↓ raw
build.sh build-log no-op 129 B view raw
run.sh run-log no-op; checks fix.diff applies 373 B view raw
env.txt environment guest uname 150 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict source trace + dead-code reachability
↓ download raw

DF-0660 β€” Heap corruption: kfree() on mbuf data pointer + leaked kmalloc in ng_device_rcvdata

Verdict: BUG CONFIRMED REAL by source trace β€” NOT reachable on the default kernel (ng_device is unshipped, unbuildable dead code)

Bug summary (source-confirmed)

ng_device_rcvdata() (sys/netgraph/ng_device.c:333-380) mismanages its scratch buffer and the inbound mbuf:

363  buffer = kmalloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);   // scratch alloc
...
369  buffer = mtod(m, char *);          // <-- OVERWRITES the kmalloc ptr with the MBUF data ptr
372      memcpy(connection->readq+connection->loc, buffer, m->m_len);
...
377  kfree(buffer, M_DEVBUF);           // <-- frees the MBUF data ptr, NOT the kmalloc
                                       //     AND no m_freem(m) -> mbuf leaked

Three distinct defects: 1. kfree() on a non-kmalloc pointer: after buffer = mtod(m,...), buffer points into the mbuf's data area (m_pktdat or a cluster), which was NOT allocated by kmalloc(...,M_DEVBUF). kfree(buffer, M_DEVBUF) hands the slab allocator a pointer it did not vend from M_DEVBUF β†’ corrupts the malloc/slab bookkeeping (on DragonFly's slab this poisons/panics or silently corrupts adjacent allocations). Repeated triggering β†’ heap corruption panic, groomable for arbitrary kernel r/w. 2. Leaked kmalloc: the line-363 scratch allocation is never freed (its only pointer was overwritten on line 369) β€” a permanent m_len-byte leak per received packet. 3. Leaked mbuf: ng_rcvdata_t contracts require the receiver to consume/free the mbuf; m_freem(m) is never called β†’ mbuf/cluster leak per packet.

m_pullup(m, m->m_len) on line 357 is also dubious (m_pullup takes a target length to make contiguous, but here the second arg is the current length).

Reachability β€” DEAD CODE on the default kernel

ng_device is not built or shipped: - It is absent from both SUBDIR lists (sys/netgraph/Makefile, sys/netgraph7/Makefile) β€” no device/ng_device entry. - ng_device.c uses the long-removed struct cdevsw API (ngd_cdevsw at :119, cdevsw_add at :152, make_dev(&ngd_cdevsw,...) at :286). struct cdevsw is not defined in modern DragonFly (sys/sys/conf.h only declares struct dev_ops), so the file does not even compile against the current tree.

So this is latent/dead code: the bug is real but cannot be triggered on any default DragonFly install. It would become a live heap-corruption primitive only if ng_device were resurrected and ported to dev_ops.

Reproduction

Not reproduced at the kernel level (the code is unshipped/unbuildable). The defects are confirmed by direct source trace. A faithful port to dev_ops (sibling finding DF-0661's ng_device_ported.c ports the same file and loads as a module) confirms the rcvdata path is reachable once ported; triggering ng_device_rcvdata requires a netgraph peer sending data on the hook (e.g. ng_echo) β€” feasible but the underlying code is dead by default, so a root-kldload-of-a-ported-module harness is artificial. See fix.diff.

Fix (fix.diff)

Drop the bogus scratch kmalloc/kfree (the mtod pointer is used directly for the memcpy) and consume the mbuf as the netgraph contract requires:

-   buffer = kmalloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);
-   if(buffer == NULL) { ... return(-1); }
-
    buffer = mtod(m, char *);
    ... memcpy(connection->readq+connection->loc, buffer, m->m_len); ...
-   kfree(buffer, M_DEVBUF);
+   m_freem(m);

Kernel references

Fix verification

not_testable

not_applicable: dead code

Confirmed kernel references

β€”

Detail

Exploit chain

none -- dead code

Evidence (decisive lines)

β€”

Verdict

Source-confirmed real. ng_device_rcvdata kfree(mbuf data ptr, M_DEVBUF) heap corruption + kmalloc leak. Dead code: ng_device unshipped, uses removed cdevsw API.