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)
PoC verification
Evidence pack
findings/poc/DF-0660 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
sys/netgraph/ng_device.c:363β bogus scratchkmalloc(later leaked)sys/netgraph/ng_device.c:369βbuffer = mtod(m, char *)overwrites the kmalloc ptrsys/netgraph/ng_device.c:377βkfree(buffer, M_DEVBUF)on the mbuf data ptr (heap corruption)sys/netgraph/ng_device.c:333-380βng_device_rcvdata(nom_freem)sys/netgraph/Makefile/sys/netgraph7/Makefileβng_devicenot in SUBDIRsys/sys/conf.hβstruct cdevswabsent (onlystruct dev_ops)
Fix verification
not_testablenot_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.
No comments yet.