NGM_SOURCE_GET_COUNTER dereferences msg->data without arglen check (latent: file is orphaned and non-compiling)
| Field | Value |
|---|---|
| ID | DF-0602 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:N |
| CWE | CWE-125 Out-of-bounds Read |
| File | sys/netgraph7/ng_source.c |
| Lines | 479-487 |
| Area | netgraph7 (ng_source packet-source node) |
| Confidence | likely |
| Discovered | 2026-07-02 |
| Reported | pending |
β Important caveat β file is currently orphaned and non-compiling
Same as DF-0601: sys/netgraph7/ng_source.c is not in sys/conf/files
and has a compile error at line 742 (ifq undeclared; should be ifsq).
The file is dead, non-compiling code; this finding is a latent-defect /
hardening item.
Summary
The GET_COUNTER handler reads *(uint8_t *)msg->data before verifying
that arglen is at least 1. Every other data-bearing command in this
switch validates arglen exactly (START:388, SETPPS:421, SET_TIMESTAMP:436,
SET_COUNTER:463, SETIFACE:409, GET_IFNAME:513). GET_COUNTER is the lone
exception and will read 1 byte past the message buffer if a peer sends the
command with arglen==0.
Root cause
sys/netgraph7/ng_source.c:479-487:
case NGM_SOURCE_GET_COUNTER:
{
uint8_t index = *(uint8_t *)msg->data; /* line 481, no arglen check */
struct ng_source_embed_cnt_info *embed;
if (index >= NG_SOURCE_COUNTERS) {
error = EINVAL;
goto done;
}
...
msg->data is the flexible-array member at the end of struct ng_mesg
(ng_message.h:81). The buffer was allocated as
kmalloc(sizeof(struct ng_mesg) + arglen, ...) via NG_MKMESSAGE
(ng_message.h:394). With arglen==0, the data area is zero bytes; reading
msg->data[0] reads 1 byte past the requested allocation. Because
kmalloc bucketizes (sizeof(struct ng_mesg)==56 bytes β 64-byte slab),
the byte lands in allocator padding rather than unmapped memory, so this
does not crash β but it is undefined behavior and the byte read is whatever
the slab layer left there. The byte only influences the index check (and
which sc->embed_counter[] slot is later returned); it is not echoed back
to the caller, so there is no direct info leak.
Threat model & preconditions
- Attacker position: privileged user (root, or any principal allowed to
send NGM_SOURCE_COOKIE control messages) sending a raw binary
NGM_SOURCE_GET_COUNTERwithheader.arglen = 0via ng_socket. - Privileges gained or impact: negligible β 1 byte of kmalloc padding is read but not returned, and the value only steers which of 4 already-privileged counters is reported (or EINVAL). No crash, no leak, no escalation.
- Required config or capabilities: a kernel with
ng_sourcecompiled in (currently impossible β file is orphaned/non-compiling). - Reachability: send a binary
ng_mesgwithheader.typecookie=NGM_SOURCE_COOKIE,header.cmd=NGM_SOURCE_GET_COUNTER,header.arglen=0, zero data bytes.
Proof of concept
No PoC can be built today (file does not compile). After fixing the
ifqβifsq typo and adding to the build, the trigger is a single
ngctl msg <node>: getcounter 0-style invocation with arglen=0 via raw
ng_socket.
Impact
- Blast radius: currently zero (file is orphaned/non-compiling).
- Severity rationale: Info β hardening / latent-defect item. The OOB read is 1 byte of allocator padding, not returned to userspace, not influencing anything beyond which slot of an already-privileged array is returned.
- Reliability: 100% once compiled in.
Recommended fix
Add an arglen lower-bound check mirroring the pattern used by every other
command in this switch:
--- a/sys/netgraph7/ng_source.c
+++ b/sys/netgraph7/ng_source.c
@@ -478,6 +478,11 @@ ng_source_rcvmsg(node_p node, item_p item, hook_p lasthook)
case NGM_SOURCE_GET_COUNTER:
{
uint8_t index;
+ struct ng_source_embed_cnt_info *embed;
+
+ if (msg->header.arglen < sizeof(index)) {
+ error = EINVAL;
+ goto done;
+ }
index = *(uint8_t *)msg->data;
struct ng_source_embed_cnt_info *embed;
if (index >= NG_SOURCE_COUNTERS) {
(A real patch should also drop the duplicate declaration of embed that
already exists on the original line 482 β the diff above keeps the change
surgical.)
Before doing this, also fix the ifqβifsq typo at line 742 and add
the file to sys/conf/files.
References
sys/netgraph7/ng_message.h:81, 394βstruct ng_mesgflexible-arraydata[]member andNG_MKMESSAGEallocation.- Every other command in
ng_source_rcvmsgswitch validatesarglenexactly βGET_COUNTERis the lone exception.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending) as a latent-defect item (file is currently orphaned/non-compiling).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0602 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | seeded: states no PoC possible (dead/non-compiling file) | 355 B | β raw |
| VERDICT.md | verdict | full narrative: latent defect confirmed by source trace; dead/orphaned/non-compiling; fix.diff authored; not_testable | 8.2 KB | β raw |
| fix.diff | suggested-fix | git-apply-able arglen lower-bound check for NGM_SOURCE_GET_COUNTER (mirrors sibling commands); matches finding proposal | 512 B | view raw |
| env.txt | environment | uname, cc version, kldload ng_source failure, ls /boot/kernel/ng_source* | 372 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-0602 β PoC: (none β file is orphaned/non-compiling)
No PoC can be built today. sys/netgraph7/ng_source.c is not listed in
sys/conf/files and has a compile error at line 742 (ifq undeclared;
should be ifsq). The file is dead, non-compiling code.
See findings/DF-0602-ng7-source-get-counter-arglen.md for the analysis
and the fix diff.
DF-0602 β VERDICT
Verdict: NOT REPRODUCED (latent code defect in dead/orphaned/non-compiling file)
The cited code defect is real and confirmed by source-level trace, but it is unreachable on this guest (and on any standard DragonFlyBSD kernel configuration) because the containing file is dead, orphaned, non-compiling code. No runtime reproduction is possible. No escalation is possible. This is a latent-defect / hardening item β exactly as the finding markdown states.
Mechanism (confirmed by source trace)
sys/netgraph7/ng_source.c:479-487 β the NGM_SOURCE_GET_COUNTER handler:
case NGM_SOURCE_GET_COUNTER:
{
uint8_t index = *(uint8_t *)msg->data; /* line 481: NO arglen check */
struct ng_source_embed_cnt_info *embed;
if (index >= NG_SOURCE_COUNTERS) { /* line 484 */
error = EINVAL;
goto done;
}
...
msg->data is the flexible-array member at the end of struct ng_mesg
(sys/netgraph7/ng_message.h:81). The message buffer was allocated by the
sender via NG_MKMESSAGE as kmalloc(sizeof(struct ng_mesg) + (len), ...)
(ng_message.h:394); header.arglen is taken verbatim from the wire and is
not re-validated by the receiver before the switch dispatches on
header.cmd. With header.arglen == 0, the data[] area is zero bytes and
*(uint8_t *)msg->data reads 1 byte past the requested allocation.
This is a genuine code defect because every other data-bearing command in
the same switch validates arglen before touching msg->data:
| Command | Line | Check |
|---|---|---|
NGM_SOURCE_START |
388 | if (msg->header.arglen != sizeof(uint64_t)) |
NGM_SOURCE_SETIFACE |
409 | if (msg->header.arglen < 2) |
NGM_SOURCE_SETPPS |
421 | if (msg->header.arglen != sizeof(uint32_t)) |
NGM_SOURCE_SET_TIMESTAMP |
436 | if (msg->header.arglen != sizeof(*embed)) |
NGM_SOURCE_SET_COUNTER |
463 | if (msg->header.arglen != sizeof(*embed)) |
NGM_ETHER_GET_IFNAME |
513 | if (msg->header.arglen < 2) |
NGM_SOURCE_GET_COUNTER |
481 | (none β reads msg->data first) |
GET_COUNTER is the lone exception.
Why it does NOT reproduce / is unreachable on this guest
The file is dead/orphaned/non-compiling. Verified facts (all confirmed on the
with-src baseline, kernel 6.5-DEVELOPMENT #0):
- Not in
sys/conf/filesβgrep -c ng_source /usr/src/sys/conf/filesβ0. There is nonetgraph7_source(or similar) option that would pull it in. The file is therefore never compiled into the static kernel. - Not in
sys/netgraph7/MakefileSUBDIR βgrep -c ng_source .../Makefileβ0. It is never built as a loadable KLD module. - No standalone module Makefile β
sys/netgraph7/ng_source.chas no siblingMakefile, unlikeecho/,pppoe/,eiface/, etc. - Compile error at line 743 β
ifq->ifq_maxlen/ifq->ifq_lenreference an undeclared identifier (the local isifsq). The file would not compile even if added to the build. - Not present as a loadable module on the running guest β
ls /boot/kernel/ng_source*β "No such file or directory";kldload -n ng_sourceβ "can't load ng_source: No such file or directory" (rc=1). NETGRAPH7is not inX86_64_GENERICβgrep -in netgraph sys/config/X86_64_GENERICβ no matches; the whole netgraph7 subsystem is opt-in viaoptions NETGRAPH7_*and is off on the default kernel.
Because the vulnerable code can never execute on this guest (or any standard
DFBSD kernel), there is no runtime to reproduce against. This is identical in
root cause and impact to the sibling finding DF-0601 (same file, same
dead-code classification, already recorded not_reproduced).
Impact assessment (latent, if the file were ever compiled in)
- The OOB read is 1 byte of
kmallocslab padding (sizeof(struct ng_mesg) == 56 β 64-byte slab; with arglen==0 the read lands in the 8-byte bucket padding, not unmapped memory, so it does not crash). - The read value only steers the subsequent
index >= NG_SOURCE_COUNTERSbounds check (i.e. which of 4 already-privileged counter slots is returned, orEINVAL). It is not echoed back to the sender βNG_MKRESPONSEcopies fromsc->embed_counter[index], not frommsg->data. - No info leak, no crash, no privilege change. Negligible latent impact.
- This is correctly classified Info severity.
No escalation (Phase 6 β N/A)
This is an OOB-read in dead code. There is no live primitive to characterize, no slab bucket to groom, no victim object to corrupt, no pointer to redirect. Phase 6 does not apply.
Recommended fix (authored as fix.diff)
Add an arglen lower-bound check mirroring the pattern of every sibling
command, placed before the msg->data dereference. The check uses
< sizeof(index) (matching SETIFACE/GET_IFNAME's lower-bound style rather
than exact-equality, since GET_COUNTER consumes only 1 byte and should
tolerate a slightly over-long message):
@@ -478,9 +478,15 @@
}
case NGM_SOURCE_GET_COUNTER:
{
- uint8_t index = *(uint8_t *)msg->data;
+ uint8_t index;
struct ng_source_embed_cnt_info *embed;
+ if (msg->header.arglen < sizeof(index)) {
+ error = EINVAL;
+ goto done;
+ }
+ index = *(uint8_t *)msg->data;
+
if (index >= NG_SOURCE_COUNTERS) {
This matches the finding markdown's ## Recommended fix proposal (same
placement, same EINVAL/goto done semantics, same sizeof(index) lower
bound). The finding's diff also notes the duplicate embed declaration; my
version keeps the single existing declaration and is surgical.
Fix validation: not_testable
A kernel build + runtime test of this single-fix diff is not possible on this guest, for reasons entirely outside this finding's scope:
- The file is not in
sys/conf/files, somake nativekernelnever compiles it β patching the arglen check has zero effect on the produced kernel. - The file has an unrelated compile error at line 743 (
ifqundeclared) that must be fixed first before the file can compile at all. - Adding the file to
conf/filesand fixing the line-743 typo are prerequisites that belong to a separate "revive netgraph7/ng_source" effort, not to this single-bug fix.
What WAS validated:
- patch -p1 --dry-run --forward < fix.diff β succeeds ("Hunk #1 succeeded
at 478") on both the host read-only sys/ tree and the in-guest
/usr/src tree.
- Applied to in-guest /usr/src, the patched region reads exactly as intended
(arglen check precedes the dereference).
- Reverted cleanly (patch -R), leaving the source pristine for the
with-src reset.
Because the bug is in dead code and cannot be exercised at runtime on any
standard kernel, fix_status = "not_testable" is the honest classification
(per the rubric: "PoC can't run on guest at all (latent/remote/missing
module); you validated the diff applies + compiles [logic] only, and traced
that it closes the code path").
PoC changes
None. The finding markdown and seeded README.md correctly state that no PoC
can be built today (file does not compile). I confirmed this and added no
trigger source β there is nothing to trigger. The only artifact authored is
fix.diff (the verified arglen-check patch).
References (confirmed during verification)
sys/netgraph7/ng_source.c:479-487β vulnerableGET_COUNTERhandler (no arglen check beforemsg->dataderef).sys/netgraph7/ng_source.c:388,409,421,436,463,513β every sibling command validatesarglenfirst (the contrast that proves the defect).sys/netgraph7/ng_source.c:743β unrelatedifqtypo that prevents compilation.sys/netgraph7/ng_message.h:69-81βstruct ng_mesgwith flexible-arraydata[].sys/netgraph7/ng_message.h:392-407βNG_MKMESSAGEallocatessizeof(struct ng_mesg) + len(solen==0β zero-bytedata[]).sys/conf/filesβng_sourceabsent (grep β 0).sys/netgraph7/Makefileβng_sourceabsent from SUBDIR (grep β 0).sys/config/X86_64_GENERICβ noNETGRAPH7options.
Fix verification
not_testablenot_testable: fix.diff patch --dry-run rc=0. Cannot build (file not in conf/files + :743 error).
patch -p1 --dry-run applies. Source-level: arglen check precedes deref.
Confirmed kernel references
- sys/netgraph7/ng_source.c:479
- sys/netgraph7/ng_source.c:481
- sys/netgraph7/ng_source.c:388
- sys/netgraph7/ng_source.c:409
- sys/netgraph7/ng_source.c:421
- sys/netgraph7/ng_source.c:436
- sys/netgraph7/ng_source.c:463
- sys/netgraph7/ng_source.c:513
- sys/netgraph7/ng_source.c:743
- sys/netgraph7/ng_message.h:69
- sys/netgraph7/ng_message.h:81
- sys/netgraph7/ng_message.h:394
Detail
Exploit chain
none -- dead code, valid hard blocker.
Evidence (decisive lines)
Source: :481 no arglen check, :388/409/421/436/463/513 all validate. grep conf/files: 0. kldload ng_source: No such file.
PoC changes
Authored: fix.diff (add arglen check before deref), VERDICT.md, manifest.json.
Verified recommended fix
Add if(msg->header.arglen < sizeof(index)){error=EINVAL;goto done;} at ng_source.c:481 before msg->data deref. Mirrors siblings. Matches finding proposal. Full diff in findings/poc/DF-0602/fix.diff.
Verdict
NOT REPRODUCED -- dead code (same file as DF-0601). NGM_SOURCE_GET_COUNTER ng_source.c:481 reads (uint8_t)msg->data without arglen check (all 6 sibling commands validate). Real defect but unreachable: not in conf/files, :743 compile error, no ng_source.ko, NETGRAPH7 not in GENERIC.
No comments yet.