CVE-2026-10637
Description
Use-after-free in Zephyr's IPv6 MLD send path lets a remote attacker trigger a denial of service or possible memory corruption via an MLDv2 General Query.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Use-after-free in Zephyr's IPv6 MLD send path lets a remote attacker trigger a denial of service or possible memory corruption via an MLDv2 General Query.
Vulnerability
In subsys/net/ip/ipv6_mld.c, the mld_send() function reads the packet interface via net_pkt_iface(pkt) after a successful net_send_data(pkt) call. The network stack's ownership contract dictates that after a successful send, ownership of the net_pkt transfers to the L2 driver, which frees the packet (e.g., ethernet_send() unrefs on success). The subsequent net_pkt_iface(pkt) thus reads freed memory. If CONFIG_NET_STATISTICS_PER_INTERFACE is enabled, the returned interface pointer is dereferenced and incremented for statistics, leading to a use-after-free. This affects all Zephyr releases through v4.4.0; the fix is merged on main but not yet released.[1]
Exploitation
An unauthenticated attacker on the local link can trigger the vulnerability without any credentials. The handler handle_mld_query(), registered for NET_ICMPV6_MLD_QUERY, responds to a valid MLDv2 General Query (unspecified multicast address, hop limit 1) by calling send_mld_report() -> mld_send(). No user interaction is required; the attacker only needs to send a crafted MLDv2 General Query packet. The freed net_pkt slot may be concurrently reallocated, causing pkt->iface to read back as NULL (NULL-pointer dereference) or as a stale/garbage pointer (memory corruption).[1]
Impact
Successful exploitation results in a remotely triggerable denial of service (crash) of the networking stack. With the right timing and heap state, there is a narrow possibility of memory corruption due to a stray increment write triggered via the per-interface statistics path (net_stats.h UPDATE_STAT/SET_STAT). The attacker does not gain code execution or information disclosure, but the networking subsystem becomes unusable or corrupted, affecting all network communication on the device.[1]
Mitigation
The fix, merged in commit 3159c53 on main, caches the interface pointer locally before calling net_send_data() and no longer touches the packet after the send. The projected fixed release is Zephyr v4.5.0; users should update to that version when available. Until then, disabling CONFIG_NET_STATISTICS_PER_INTERFACE removes the exploitable code path, but does not fix the underlying use-after-free. No KEV listing exists. The IPv4/IGMP sibling had already used the correct pattern.[1][2]
AI Insight generated on Jun 16, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)
Patches
53159c53e8e7dnet: ipv6: mld: fix use-after-free
1 file changed · +5 −4
subsys/net/ip/ipv6_mld.c+5 −4 modified@@ -125,23 +125,24 @@ static int mld_create_packet(struct net_pkt *pkt, uint16_t count) static int mld_send(struct net_pkt *pkt) { + __maybe_unused struct net_if *iface = net_pkt_iface(pkt); int ret; net_pkt_cursor_init(pkt); net_ipv6_finalize(pkt, NET_IPPROTO_ICMPV6); ret = net_send_data(pkt); if (ret < 0) { - net_stats_update_icmp_drop(net_pkt_iface(pkt)); - net_stats_update_ipv6_mld_drop(net_pkt_iface(pkt)); + net_stats_update_icmp_drop(iface); + net_stats_update_ipv6_mld_drop(iface); net_pkt_unref(pkt); return ret; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); - net_stats_update_ipv6_mld_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); + net_stats_update_ipv6_mld_sent(iface); return 0; }
aaed8332a62bnet: ipv6: nbr: fix use-after-free
1 file changed · +3 −3
subsys/net/ip/ipv6_nbr.c+3 −3 modified@@ -1222,7 +1222,7 @@ int net_ipv6_send_na(struct net_if *iface, const struct net_in6_addr *src, goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2177,7 +2177,7 @@ int net_ipv6_send_ns(struct net_if *iface, net_ipv6_nbr_unlock(); - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2249,7 +2249,7 @@ int net_ipv6_send_rs(struct net_if *iface) goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0;
a96ab665bd57net: ipv6: nbr: fix use-after-free
1 file changed · +3 −3
subsys/net/ip/ipv6_nbr.c+3 −3 modified@@ -1222,7 +1222,7 @@ int net_ipv6_send_na(struct net_if *iface, const struct net_in6_addr *src, goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2158,7 +2158,7 @@ int net_ipv6_send_ns(struct net_if *iface, net_ipv6_nbr_unlock(); - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2230,7 +2230,7 @@ int net_ipv6_send_rs(struct net_if *iface) goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0;
3cab8170460fnet: ipv6: nbr: fix use-after-free
1 file changed · +3 −3
subsys/net/ip/ipv6_nbr.c+3 −3 modified@@ -1221,7 +1221,7 @@ int net_ipv6_send_na(struct net_if *iface, const struct in6_addr *src, goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2169,7 +2169,7 @@ int net_ipv6_send_ns(struct net_if *iface, net_ipv6_nbr_unlock(); - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2241,7 +2241,7 @@ int net_ipv6_send_rs(struct net_if *iface) goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0;
ef293d8b5e5dnet: ipv6: nbr: fix use-after-free
1 file changed · +3 −3
subsys/net/ip/ipv6_nbr.c+3 −3 modified@@ -1135,7 +1135,7 @@ int net_ipv6_send_na(struct net_if *iface, const struct in6_addr *src, goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2063,7 +2063,7 @@ int net_ipv6_send_ns(struct net_if *iface, net_ipv6_nbr_unlock(); - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0; @@ -2135,7 +2135,7 @@ int net_ipv6_send_rs(struct net_if *iface) goto drop; } - net_stats_update_icmp_sent(net_pkt_iface(pkt)); + net_stats_update_icmp_sent(iface); net_stats_update_ipv6_nd_sent(iface); return 0;
Vulnerability mechanics
Root cause
"Use-after-free of net_pkt in IPv6 MLD send path: `mld_send()` reads `pkt->iface` after `net_send_data()` has transferred ownership and freed the packet."
Attack vector
An attacker on the local link (no authentication needed) sends an MLDv2 General Query — an ICMPv6 packet with an unspecified multicast address and hop limit 1 [ref_id=1]. The handler `handle_mld_query()` calls `send_mld_report()` → `mld_send()`, which frees the packet on successful transmit and then reads the freed packet's `iface` pointer, triggering a use-after-free [CWE-416]. When `CONFIG_NET_STATISTICS_PER_INTERFACE` is enabled, the stale pointer is dereferenced for statistics updates, causing either a NULL-pointer crash or memory corruption.
Affected code
subsys/net/ip/ipv6_mld.c in `mld_send()` and patches `[patch_id=6189724]` through `[patch_id=6189841]` show the fix applied across MLD and neighbor-discovery (`ipv6_nbr.c`). The advisory [ref_id=1] identifies the root as `mld_send()` calling `net_pkt_iface(pkt)` after `net_send_data(pkt)` returns success, violating the ownership contract documented in `include/zephyr/net/net_core.h`.
What the fix does
The patch `[patch_id=6189724]` stores `net_pkt_iface(pkt)` in a local variable `iface` before `net_send_data()` is called, then uses that cached pointer in all statistics macros (`net_stats_update_icmp_sent(iface)`, etc.) on both the success and error paths [ref_id=2]. The same pattern is applied to the neighbor-discovery send functions (`send_na`, `send_ns`, `send_rs`) in `[patch_id=6189725]`, `[patch_id=6189726]`, and `[patch_id=6189727]`. After the fix, the packet is never touched following the send call, closing the use-after-free window.
Preconditions
- configCONFIG_NET_STATISTICS_PER_INTERFACE must be enabled to reach the vulnerable dereference path
- networkAttacker must be on the same Layer-2 link (no authentication required)
- inputAttacker sends a valid MLDv2 General Query ICMPv6 packet (unspecified multicast address, hop limit 1)
Generated on Jun 16, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.