VYPR
Medium severity5.9NVD Advisory· Published Jun 16, 2026· Updated Jun 16, 2026

CVE-2026-10638

CVE-2026-10638

Description

Use-after-free in Zephyr's ICMPv6 processing allows remote denial of service via crafted IPv6 packets.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Use-after-free in Zephyr's ICMPv6 processing allows remote denial of service via crafted IPv6 packets.

Vulnerability

The bug resides in subsys/net/ip/icmpv6.c, specifically in the functions icmpv6_handle_echo_request() and net_icmpv6_send_error(). After calling net_try_send_data(), these functions attempt to update network statistics using net_pkt_iface() on the just-sent packet. However, net_try_send_data() may free the packet back to its memory slab, leading to a use-after-free when net_pkt_iface() is called. This affects Zephyr networking builds with CONFIG_NET_NATIVE_IPV6 enabled, approximately versions v4.2.0 through v4.4.0. The same pattern exists in ICMPv4 and was fixed in a sibling commit [1].

Exploitation

An unauthenticated remote attacker can trigger the vulnerability by sending an ICMPv6 Echo Request (ping) or any IPv6 packet that causes an ICMPv6 error response (e.g., unknown next header, fragment reassembly timeout, destination unreachable). No special network position or authentication is required. The attacker simply sends the crafted packet; the vulnerable code path executes in the RX thread, leading to the use-after-free.

Impact

A successful exploit results in a use-after-free read and, when statistics per interface are enabled (CONFIG_NET_STATISTICS_PER_INTERFACE), a subsequent write through a stale iface pointer. This can cause a system crash (denial of service) or potential memory corruption. The exact impact depends on the memory allocator state at the time of the attack, but the vulnerability is rated with a CVSS v3 score of 5.9 (Medium) due to the remote trigger vector and potential for corruption.

Mitigation

The fix is provided in commit 09c8578c66b517c5165cde53332ed5d8d8ef2cfc (Zephyr RTOS). The patch caches the interface pointer (iface) before the call to net_try_send_data() and uses the cached pointer for all subsequent statistics updates, preventing the use-after-free. Affected users should upgrade to a version containing this fix; if not yet available, a workaround may include disabling ICMPv6 processing or building without CONFIG_NET_STATISTICS_PER_INTERFACE, though this reduces functionality and does not eliminate the read use-after-free. No KEV listing is known at this time.

--- Based solely on the provided CVE description and reference [1].

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

Patches

1
09c8578c66b5

net: ip: icmpv6: fix use-after-free

https://github.com/zephyrproject-rtos/zephyrTim PamborApr 10, 2026via nvd-ref
1 file changed · +10 10
  • subsys/net/ip/icmpv6.c+10 10 modified
    @@ -113,6 +113,7 @@ static enum net_verdict icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
     {
     	struct net_pkt *reply = NULL;
     	struct net_ipv6_hdr *ip_hdr = hdr->ipv6;
    +	struct net_if *iface = net_pkt_iface(pkt);
     	struct net_in6_addr req_src, req_dst;
     	const struct net_in6_addr *src;
     	struct net_pkt_cursor backup;
    @@ -137,7 +138,7 @@ static enum net_verdict icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
     		goto drop;
     	}
     
    -	reply = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), payload_len,
    +	reply = net_pkt_alloc_with_buffer(iface, payload_len,
     					  NET_AF_INET6, NET_IPPROTO_ICMPV6,
     					  PKT_WAIT_TIME);
     	if (!reply) {
    @@ -146,8 +147,7 @@ static enum net_verdict icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
     	}
     
     	if (net_ipv6_is_addr_mcast_raw(ip_hdr->dst)) {
    -		src = net_if_ipv6_select_src_addr(net_pkt_iface(pkt),
    -						  &req_src);
    +		src = net_if_ipv6_select_src_addr(iface, &req_src);
     
     		if (net_ipv6_is_addr_unspecified(src)) {
     			NET_DBG("DROP: No src address match");
    @@ -189,7 +189,7 @@ static enum net_verdict icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
     		goto drop;
     	}
     
    -	net_stats_update_icmp_sent(net_pkt_iface(reply));
    +	net_stats_update_icmp_sent(iface);
     
     	net_pkt_cursor_restore(pkt, &backup);
     	return NET_CONTINUE;
    @@ -199,7 +199,7 @@ static enum net_verdict icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
     		net_pkt_unref(reply);
     	}
     
    -	net_stats_update_icmp_drop(net_pkt_iface(pkt));
    +	net_stats_update_icmp_drop(iface);
     
     	return NET_DROP;
     }
    @@ -209,6 +209,7 @@ int net_icmpv6_send_error(struct net_pkt *orig, uint8_t type, uint8_t code,
     {
     	NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
     	int err = -EIO;
    +	struct net_if *iface = net_pkt_iface(orig);
     	struct net_in6_addr orig_src, orig_dst;
     	struct net_ipv6_hdr *ip_hdr;
     	const struct net_in6_addr *src;
    @@ -258,7 +259,7 @@ int net_icmpv6_send_error(struct net_pkt *orig, uint8_t type, uint8_t code,
     		copy_len = net_pkt_get_len(orig);
     	}
     
    -	pkt = net_pkt_alloc_with_buffer(net_pkt_iface(orig),
    +	pkt = net_pkt_alloc_with_buffer(iface,
     					net_pkt_lladdr_src(orig)->len * 2 +
     					copy_len + NET_ICMPV6_UNUSED_LEN,
     					NET_AF_INET6, NET_IPPROTO_ICMPV6,
    @@ -310,8 +311,7 @@ int net_icmpv6_send_error(struct net_pkt *orig, uint8_t type, uint8_t code,
     	net_pkt_lladdr_dst(pkt)->len = net_pkt_lladdr_src(orig)->len;
     
     	if (net_ipv6_is_addr_mcast_raw(ip_hdr->dst)) {
    -		src = net_if_ipv6_select_src_addr(net_pkt_iface(pkt),
    -						  &orig_dst);
    +		src = net_if_ipv6_select_src_addr(iface, &orig_dst);
     	} else {
     		src = &orig_dst;
     	}
    @@ -347,15 +347,15 @@ int net_icmpv6_send_error(struct net_pkt *orig, uint8_t type, uint8_t code,
     		net_sprint_ipv6_addr(&orig_src));
     
     	if (net_try_send_data(pkt, K_NO_WAIT) >= 0) {
    -		net_stats_update_icmp_sent(net_pkt_iface(pkt));
    +		net_stats_update_icmp_sent(iface);
     		return 0;
     	}
     
     drop:
     	net_pkt_unref(pkt);
     
     drop_no_pkt:
    -	net_stats_update_icmp_drop(net_pkt_iface(orig));
    +	net_stats_update_icmp_drop(iface);
     
     	return err;
     }
    

Vulnerability mechanics

Root cause

"Use-after-free: `net_pkt_iface()` is called on a `net_pkt` after it has been handed to `net_try_send_data()`, which may free the packet, causing a read of freed memory and, with statistics enabled, an attacker-influenceable write through the stale interface pointer."

Attack vector

An unauthenticated remote attacker simply sends an ICMPv6 Echo Request (ping) or crafts an IPv6 packet that forces an ICMPv6 error response (e.g., unknown next header, fragment reassembly timeout, or destination unreachable). The target must run Zephyr with `CONFIG_NET_NATIVE_IPV6` and without a TX queue (`CONFIG_NET_TC_TX_COUNT == 0`) or with an L2/driver that frees the packet asynchronously. No authentication or special network position is required beyond reachability. [ref_id=1]

Affected code

`subsys/net/ip/icmpv6.c` — `icmpv6_handle_echo_request()` and `net_icmpv6_send_error()` both called `net_pkt_iface()` on a packet after that packet was handed to `net_try_send_data()` (or after a send attempt). The send path may free the packet synchronously or asynchronously, making the subsequent `net_pkt_iface()` call a use-after-free read of the `net_pkt` structure and, when `CONFIG_NET_STATISTICS_PER_INTERFACE` is enabled, a write through the stale iface pointer (`iface-stats.icmp.sent++`).

What the fix does

The patch caches the interface pointer (`struct net_if *iface`) before any send call and passes the cached pointer to all statistics-update macros (`net_stats_update_icmp_sent` / `net_stats_update_icmp_drop`) instead of calling `net_pkt_iface()` after the send. It also uses the cached pointer for packet allocation and address selection. This ensures the iface is always read from a live packet, eliminating the use-after-free. [patch_id=6189723]

Preconditions

  • configCONFIG_NET_NATIVE_IPV6 enabled
  • configCONFIG_NET_STATISTICS_PER_INTERFACE enabled (for write primitive)
  • configCONFIG_NET_TC_TX_COUNT == 0 or driver frees packet asynchronously

Generated on Jun 16, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.