sizeof(ips) pointer-vs-struct typo in NGM_CISCO_GET_IPADDR response sizing
| Field | Value |
|---|---|
| ID | DF-0608 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N |
| CWE | CWE-131 Incorrect Calculation of Buffer Size |
| File | sys/netgraph7/iface/ng_iface.c |
| Lines | 724 |
| Area | netgraph7 (ng_iface virtual interface node) |
| Confidence | certain |
| Discovered | 2026-07-02 |
| Reported | pending |
Summary
NGM_CISCO_GET_IPADDR allocates the response with sizeof(ips), where
ips is a pointer variable just declared in the loop body, instead of
sizeof(*ips) or sizeof(struct ng_cisco_ipaddr). On x86_64 (the only
platform DragonFlyBSD currently targets) the two sizes coincidentally
match (8 bytes), so there is no current memory-safety impact; but it is a
real typo that becomes a 4-byte heap overflow the day 32-bit support
returns or the struct grows.
Root cause
In ng_iface_rcvmsg, case NGM_CISCO_GET_IPADDR
(sys/netgraph7/iface/ng_iface.c:713-735):
713: TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
714: struct ifaddr *ifa = ifac->ifa;
715: struct ng_cisco_ipaddr *ips; /* <-- pointer */
...
724: NG_MKRESPONSE(resp, msg, sizeof(ips), M_WAITOK | M_NULLOK);
...
727: ips = (struct ng_cisco_ipaddr *)resp->data;
728: ips->ipaddr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; /* 4 bytes at offset 0 */
729: ips->netmask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;/* 4 bytes at offset 4 */
sizeof(ips) is sizeof(struct ng_cisco_ipaddr *), which is 8 on
x86_64 and 4 on i386. NG_MKRESPONSE
(sys/netgraph7/ng_message.h:410-424) allocates sizeof(struct ng_mesg) +
len and sets header.arglen = len. The subsequent two assignments write a
total of sizeof(struct ng_cisco_ipaddr) = 8 bytes (two struct in_addr,
see sys/netgraph7/cisco/ng_cisco.h:65-68). On x86_64 this exactly fills the
8-byte body by coincidence; on i386 it would overflow the 4-byte body by 4
bytes (heap corruption in M_NETGRAPH_MSG, plus the consumer only reads 4
bytes of body so the netmask would silently not round-trip). The upstream
FreeBSD ng_iface.c uses sizeof(struct ng_cisco_ipaddr) β this is a
DragonFly-specific regression.
Threat model & preconditions
- Attacker position: any local user with netgraph access (ng_socket /
ngctl) who can issue
NGM_CISCO_GET_IPADDRon anng_ifacenode. - Privileges gained or impact: no security impact on any platform
DragonFlyBSD currently targets (only
x86_64/vkernel64/pc64exist undersys/platformandsys/cpu, all 64-bit). It is a latent buffer-size miscalculation (CWE-131) that would turn into a 4-byte heap overflow if 32-bit support were reintroduced or ifstruct ng_cisco_ipaddrever gained a field. - Required config or capabilities: netgraph access.
- Reachability:
ngctl msg <ng_iface_node>: getipaddr(or the equivalent rawNGM_CISCO_GET_IPADDRvia ng_socket).
Proof of concept
Not exploitable on supported platforms. Demonstrating the regression vs
upstream: grep shows the only call site is here; replacing
sizeof(ips) with sizeof(*ips) changes nothing on x86_64 but is clearly
the intended expression. A build with -m32 on a hypothetical i386 kernel
module would write 8 bytes into a 4-byte kmalloc'd body; that is the
latent overflow.
Impact
- Blast radius: currently zero (all supported platforms are 64-bit).
- Severity rationale: Info β hardening item. No demonstrated impact on
supported platforms; latent
CWE-131that could bite if 32-bit support returns or the struct grows.
Recommended fix
--- a/sys/netgraph7/iface/ng_iface.c
+++ b/sys/netgraph7/iface/ng_iface.c
@@ -721,7 +721,7 @@ ng_iface_rcvmsg(node_p node, item_p item, hook_p lasthook)
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
- NG_MKRESPONSE(resp, msg, sizeof(ips), M_WAITOK | M_NULLOK);
+ NG_MKRESPONSE(resp, msg, sizeof(*ips), M_WAITOK | M_NULLOK);
if (resp == NULL) {
error = ENOMEM;
break;
References
sys/netgraph7/cisco/ng_cisco.h:65-68βstruct ng_cisco_ipaddr(twostruct in_addr, total 8 bytes).- Upstream FreeBSD
ng_iface.cusessizeof(struct ng_cisco_ipaddr)β the correct expression.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending) as a hardening item.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0608 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| verify_typo.sh | trigger-source | grep-confirms typo at both sites + inline userspace size-coincidence proof | 2.1 KB | view raw |
| build.sh | build-script | compiles the inline size-proof helper | 800 B | view raw |
| run.sh | run-script | runs verify_typo.sh against /usr/src | 321 B | view raw |
| run.log | run-log | full verification output on the live guest | 725 B | view raw |
| env.txt | environment | uname + cc version | 188 B | view raw |
| fix.diff | suggested-fix | sizeof(ips)->sizeof(*ips) in BOTH netgraph7:724 and netgraph:701 (git-apply-able) | 826 B | view raw |
| fix_build.log | build-log | ng_iface module rebuild with fix, rc=0, clean -Werror compile | 6.7 KB | view raw |
| VERDICT.md | verdict | full narrative: mechanism, twin-typo, reachability, fix validation | 5.7 KB | β raw |
| README.md | readme | original PoC readme (no runtime PoC - Info finding) | 542 B | β 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-0608 β PoC: (none β no impact on supported platforms)
No PoC. DF-0608 documents a sizeof(ips) vs sizeof(*ips) typo that has
no memory-safety impact on any platform DragonFlyBSD currently targets
(all supported platforms are 64-bit, where sizeof(pointer) ==
sizeof(struct ng_cisco_ipaddr) == 8). It is a latent CWE-131 that would
turn into a 4-byte heap overflow if 32-bit support returned or the struct
gained a field.
See findings/DF-0608-ng7-iface-cisco-get-ipaddr-sizeof-typo.md for the
analysis and the one-line fix.
DF-0608 β VERDICT
Verdict: REPRODUCED (source-level). Info-severity CWE-131 typo confirmed; zero runtime/security impact on supported platforms.
The claim
NGM_CISCO_GET_IPADDR in ng_iface_rcvmsg sizes its netgraph response with
sizeof(ips), where ips is a pointer (struct ng_cisco_ipaddr *ips),
instead of sizeof(*ips) or sizeof(struct ng_cisco_ipaddr). The finding
flags this as a latent CWE-131 (Incorrect Calculation of Buffer Size) that
has no impact on any platform DragonFlyBSD currently targets (all 64-bit).
Confirmation (source-level, traced line-by-line)
The cited typo is real and present at sys/netgraph7/iface/ng_iface.c:724:
720: struct ng_cisco_ipaddr *ips; /* pointer */
...
724: NG_MKRESPONSE(resp, msg, sizeof(ips), M_WAITOK | M_NULLOK);
...
729: ips = (struct ng_cisco_ipaddr *)resp->data;
730: ips->ipaddr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; /* 4 bytes @0 */
731: ips->netmask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; /* 4 bytes @4 */
sizeof(ips)=sizeof(struct ng_cisco_ipaddr *)= 8 on amd64, 4 on i386.NG_MKRESPONSE(sys/netgraph7/ng_message.h:410-424) allocatessizeof(struct ng_mesg) + lenand setsheader.arglen = len.struct ng_cisco_ipaddr(sys/netgraph7/cisco/ng_cisco.h:65-68) = twostruct in_addr= 8 bytes.
On amd64: len = sizeof(ips) = 8 == sizeof(struct ng_cisco_ipaddr) = 8 β the
8-byte body is exactly filled by the two in_addr writes. No overflow, no
wrong-size copy, no observable defect. This was verified at runtime with a
userspace size-proof (run.log step 5):
sizeof(struct ng_cisco_ipaddr) = 8 sizeof(*ips) = 8 sizeof(ips) [pointer, BUGGY] = 8 VERDICT: on this platform sizes coincide -> NO runtime/security impact
On a hypothetical i386 build: len = sizeof(ips) = 4, but the handler writes
8 bytes β a 4-byte heap overflow into the M_NETGRAPH_MSG slab, and the
consumer would only read 4 bytes of body (silent netmask loss). DragonFlyBSD
has no 32-bit platform under sys/platform/sys/cpu, so this is latent.
Additional finding (twin typo)
The identical sizeof(ips) typo exists in the old-netgraph copy at
sys/netgraph/iface/ng_iface.c:701:
697: struct ng_cisco_ipaddr *ips;
...
701: NG_MKRESPONSE(resp, msg, sizeof(ips), M_NOWAIT);
This copy is built as the loadable module /boot/kernel/ng_iface.ko on the
running guest (the netgraph7 ng_iface.c is optional netgraph7_iface and no
netgraph7 modules are installed β confirmed: ls /boot/kernel/ | grep ng7 β
empty). So the live-reachable instance of this typo is the old-netgraph one;
the cited netgraph7 line is the same defect in sibling code. The fix covers both.
Reachability on this guest
ng_iface.c(both copies) isoptional netgraph*_ifaceβ a module, not inX86_64_GENERIC./boot/kernel/ng_iface.ko(old netgraph) exists but is not loaded by default (kldstat | grep ngβ empty).- Reachable path:
kldload ng_ifaceβ create anng_ifacenode viang_socket/ngctlβ assign it an IPv4 address βngctl msg <node>: getipaddr. Requires netgraph access (any local user withng_socket).
No escalation chain applies β this is a pure correctness/hardening typo, not a write-capable primitive on any supported platform.
Exploit chain
None. This is not a memory-corruption primitive on any platform DragonFlyBSD
targets. No uid=0 is derivable. Documented impact ceiling: zero on amd64;
latent 4-byte heap overflow on a hypothetical i386 port.
Fix validation (Phase 8)
Authored fix.diff changing sizeof(ips) β sizeof(*ips) in both copies
(cited netgraph7 + twin netgraph). git apply --check β clean.
Validated in-guest on the unpatched with-src baseline (6.5-DEVELOPMENT #0):
- Baseline (before): typo present in both source files:
-
sys/netgraph7/iface/ng_iface.c:724:NG_MKRESPONSE(resp, msg, sizeof(ips), ...)-sys/netgraph/iface/ng_iface.c:701:NG_MKRESPONSE(resp, msg, sizeof(ips), ...) - Applied fix.diff (
patch -p1): both hunks succeeded at lines 721 and 698. - After: both lines read
sizeof(*ips)β typo gone. - Built
ng_ifacemodule (makeinsys/netgraph/iface/): rc=0, clean compile under the kernel's strict flags (-Werror -Wpointer-arith -Wcast-qual -Wstrict-prototypes ...), producing a valid 13016-byteng_iface.kowithng_iface_rcvmsgpresent. Full build log infix_build.log.
No runtime before/after test is possible or meaningful on amd64: because
sizeof(ips) == sizeof(*ips) == 8, the fixed and unfixed modules produce
byte-identical behavior. The validation is therefore source-level: the
defect is present in the baseline source and absent in the patched source, and
the patched module compiles cleanly. This is the correct and complete
validation for an Info-severity, zero-runtime-impact hardening fix.
Recommended fix
fix.diff (in this folder): sizeof(ips) β sizeof(*ips) in
sys/netgraph7/iface/ng_iface.c:724 and sys/netgraph/iface/ng_iface.c:701.
This supersedes the finding markdown's proposal (which covered only the
netgraph7 copy) by also fixing the identical twin typo in the old-netgraph
copy that is actually built into the installed /boot/kernel/ng_iface.ko.
PoC changes
The shipped README declared "No PoC." Added:
- verify_typo.sh β grep-confirms the typo at both sites + an inline
userspace compile of the size coincidence proof.
- build.sh / run.sh β exact reproducible build/run wrappers.
- run.log β full verification output on the live guest.
- fix.diff / fix_build.log β the validated fix + clean module build log.
Fix verification
fixedVALIDATED: baseline sizeof(ips) at 2 sites; patched sizeof(*ips). ng_iface.ko rebuild rc=0 -Werror.
BEFORE: sizeof(ips). AFTER: sizeof(*ips). Module rebuild rc=0.
Confirmed kernel references
Detail
Exploit chain
none -- no impact on amd64 (sizes coincide). CWE-131 latent on i386 only.
Evidence (decisive lines)
sizeof(ips)=8, sizeof(*ips)=8, sizeof(struct ng_cisco_ipaddr)=8. Both copies have typo. Module rebuild rc=0.
PoC changes
Authored: verify_typo.sh (grep + inline sizeof proof), fix.diff (sizeof(ips)->sizeof(*ips) both copies), VERDICT.md, manifest.json.
Verified recommended fix
Change sizeof(ips) to sizeof(*ips) at ng_iface.c:724 (netgraph7) AND :701 (old netgraph). Supersedes finding (adds twin old-netgraph copy). Full diff in findings/poc/DF-0608/fix.diff.
Verdict
REPRODUCED (source-level). sizeof(ips) pointer-vs-struct typo at ng_iface.c:724 (netgraph7) + :701 (old netgraph). On amd64 sizeof(ips)==sizeof(*ips)==8 -> NO runtime impact. Latent CWE-131 on i386 only. Twin typo in old-netgraph copy is the live one.
No comments yet.