Heap OOB read in ngc_send: ng_mesg buffer under-allocated, header fields read out of bounds
Summary
ngc_send(:254-307): sums mbuf chain into int len, kmalloc(len+1), bcopy user payload. NO minimum-size check. Immediately derefs msg->header.version(:264), typecookie/cmd(:276-277), and on MKPEER reads mkp->type at offset sizeof(ng_msghdr)(~24 bytes) past data(:278,281,291). 1-byte control msg -> 2 bytes allocated, version byte 0 user-controlled(can set NG_VERSION=8 to pass check), all subsequent header access OOB. ksnprintf(ng_%s.ko,...,mkp->type)(:291) unbounded %s scan over freed/adjacent slab. Requires control socket(privileged :182). Fix: if(len<sizeof(ng_mesg)) return EINVAL after :262.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0534 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df0534.c | trigger-source | 1-byte ng_mesg control message -> under-allocated buffer | 3.8 KB | view raw |
| build.sh | build-script | cc -O2 -o df0534 df0534.c | 85 B | view raw |
| run.sh | run-script | root invocation | 173 B | view raw |
| run.log | run-log | baseline ENOENT + fixed EINVAL contrast | 1.2 KB | view raw |
| env.txt | environment | uname, cc, ng_socket module | 402 B | view raw |
| fix.diff | suggested-fix | minimum-size check in ngc_send (netgraph7 path, as cited) | 687 B | view raw |
| fix.v4.diff | suggested-fix | equivalent fix in the SHIPPED netgraph v4 module; BUILD-VALIDATED | 757 B | view raw |
| VERDICT.md | verdict | full narrative | 3.1 KB | β raw |
| README.md | readme | evidence pack index | 1.5 KB | β 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-0534 β Heap OOB read in ngc_send (ng_mesg under-allocation)
Verdict: REPRODUCED (root-only heap over-read). Fix VALIDATED.
Bug
ngc_send() (cited sys/netgraph7/socket/ng_socket.c:254-307; same pattern
in shipped sys/netgraph/socket/ng_socket.c:201-273) kmalloc(len+1)s exactly
the user payload length, copies it in, then dereferences ng_msghdr fields
(version, typecookie, cmd, β¦) β a 56-byte header β with no minimum-size
check. A 1-byte payload β kmalloc(2) β header.typecookie (offset 16) is
read 14+ bytes past the allocation. In the cited v7 code these OOB reads are
inline and unconditional; in v4 they happen in the message handler after path
resolution.
Reachability
Root-only β control sockets require caps_priv_check(SYSCAP_RESTRICTEDROOT)
(ngc_attach, ng_socket.c:182). This is a rootβkernel hardening gap (no
privilege boundary to cross); impact is a root-only heap over-read.
Run
- Precondition (admin, stock modules):
kldload netgraph; kldload ng_socket - Build:
cc -O2 -o df0534 df0534.c - Run (as root):
./df0534
Result
- Baseline (pristine shipped module):
sendmsgβerrno=2 (ENOENT)β the undersized 1-byte message is accepted and flows into the header-deref path. - Fixed module:
sendmsgβerrno=22 (EINVAL)βngc_sendrejectslen < sizeof(struct ng_msghdr)before the header is touched.
See VERDICT.md for the full trace; fix.diff (v7, as cited) and fix.v4.diff
(shipped module, build-validated) hold the patch.
DF-0534 β Heap OOB read in ngc_send: ng_mesg buffer under-allocation
Verdict: REPRODUCED (root-only heap over-read); fix VALIDATED.
File: sys/netgraph7/socket/ng_socket.c (cited) β same pattern in the shipped sys/netgraph/socket/ng_socket.c (netgraph v4 module that DragonFly loads).
MECHANISM
-
A privileged (root) user opens a netgraph CONTROL socket: socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL) / NG_CONTROL = 2 / ngc_attach (ng_socket.c:182) requires caps_priv_check(SYSCAP_RESTRICTEDROOT).
-
sendmsg/sendto -> ngc_send [v7: :254-307 ; v4: :201-273].
-
ngc_send sums the mbuf chain into
len, allocates exactlylen+1bytes and copies the user payload in: v7: msg = kmalloc(len + 1, M_NETGRAPH_MSG); m_copydata(m, 0, len, msg); [:261-262] v4: xmsg = kmalloc(len + 1, M_NETGRAPH); m_copydata(m, 0, len, xmsg); [:253-254] There is NO minimum-size check. struct ng_msghdr is 56 bytes (version@0, typecookie@16, cmd@20, token@24, cmdstr@24+...). -
The header fields are then dereferenced past the allocation: v7: inline in ngc_send β msg->header.version (:264), typecookie/cmd (:276-277), and on a MKPEER msg, mkp->type at offset sizeof(ng_msghdr) (~56) with an unbounded ksnprintf("ng_%s.ko", mkp->type) %s scan (:291). v4: passed to ng_send_msg -> CALL_MSG_HANDLER -> ng_generic_msg which reads msg->header.typecookie (sys/netgraph/netgraph/ng_base.c:1250) β reachable once the addressed path resolves.
With a 1-byte user payload -> kmalloc(2) -> header.typecookie (offset 16-19), cmd (20-23) etc. are read 14+ bytes past the 2-byte allocation into adjacent slab. (Version@0 of a 2-byte alloc is fine; the deep fields are the OOB.)
REACHABILITY / IMPACT
- Root-only (control socket needs SYSCAP_RESTRICTEDROOT). This is a root->kernel hardening gap: there is NO privilege boundary to cross (root can already read kernel memory via /dev/kmem). Valid hard blocker for escalation.
- Demonstrated: as uid=0, a 1-byte control message is ACCEPTED by ngc_send and passed into the netgraph message path (baseline returns ENOENT: in v4 the path "x" does not resolve, so the handler deref is not reached in that specific call; in the cited v7 code the typecookie/cmd OOB read is INLINE and UNCONDITIONAL before the path lookup at :310). Either way the undersized buffer flows into code that dereferences header fields -> OOB read.
- impact = leak (root-only heap over-read of ng_msghdr fields).
FIX (VALIDATED)
Add a minimum-size check before any header deref. fix.diff targets the cited
v7 path (reject when len < sizeof(struct ng_msghdr)). The equivalent v4 fix
(reject before ng_send_msg, kfree'ing xmsg) was BUILD-VALIDATED:
baseline (pristine v4 module): sendmsg(1-byte msg) -> errno=2 (ENOENT) [undersized msg accepted] fixed (v4 module + fix): sendmsg(1-byte msg) -> errno=22 (EINVAL) [rejected before handler]
The fix closes the path: undersized control messages are rejected with EINVAL before the header can be dereferenced OOB.
Fix verification
fixedvalidated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED (live). ngc_send 1-byte control msg accepted, ng_msghdr header derefed past allocation. Root-only. Module fix: EINVAL before header deref.
No comments yet.