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

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_IPADDR on an ng_iface node.
  • Privileges gained or impact: no security impact on any platform DragonFlyBSD currently targets (only x86_64 / vkernel64 / pc64 exist under sys/platform and sys/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 if struct ng_cisco_ipaddr ever gained a field.
  • Required config or capabilities: netgraph access.
  • Reachability: ngctl msg <ng_iface_node>: getipaddr (or the equivalent raw NGM_CISCO_GET_IPADDR via 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-131 that could bite if 32-bit support returns or the struct grows.
--- 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 (two struct in_addr, total 8 bytes).
  • Upstream FreeBSD ng_iface.c uses sizeof(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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0608 Β· 11 files
FileTypeDescriptionSize
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
README.md readme original PoC readme (no runtime PoC - Info finding)
↓ download 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.

VERDICT.md verdict full narrative: mechanism, twin-typo, reachability, fix validation
↓ download raw

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 */

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) is optional netgraph*_iface β€” a module, not in X86_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 an ng_iface node via ng_socket/ngctl β†’ assign it an IPv4 address β†’ ngctl msg <node>: getipaddr. Requires netgraph access (any local user with ng_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):

  1. 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), ...)
  2. Applied fix.diff (patch -p1): both hunks succeeded at lines 721 and 698.
  3. After: both lines read sizeof(*ips) β€” typo gone.
  4. Built ng_iface module (make in sys/netgraph/iface/): rc=0, clean compile under the kernel's strict flags (-Werror -Wpointer-arith -Wcast-qual -Wstrict-prototypes ...), producing a valid 13016-byte ng_iface.ko with ng_iface_rcvmsg present. Full build log in fix_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.

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

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: 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.
↓ fix.diff6.5-DEVELOPMENT #0 (module rebuild only, not in GENERIC)

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.