Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-10646

CVE-2026-10646: Zephyr RTOS Use-After-Free Vulnerability

CVE-2026-10646 is a use-after-free vulnerability in Zephyr RTOS BSD-sockets getaddrinfo() implementation. Attackers can exploit stack corruption to cause denial of service or memory corruption through DNS responses.

Published:

CVE-2026-10646 Overview

CVE-2026-10646 is a network-influenceable use-after-return vulnerability in Zephyr RTOS's BSD-sockets getaddrinfo() implementation. The socket layer passes a pointer to a stack-allocated struct getaddrinfo_state ai_state as user_data to an asynchronous DNS resolver query. When the semaphore wait times out with -EAGAIN, the pre-fix code retries the query without cancelling the previous one, leaving a stale stack pointer registered as the resolver callback. A subsequent spoofable UDP DNS response or delayed timeout work invokes dns_resolve_cb() against an out-of-scope stack frame [CWE-416]. The flaw affects Zephyr versions v4.0.0 through v4.4.0.

Critical Impact

Attackers on- or off-path can trigger memory corruption or denial of service by replaying or spoofing a 16-bit DNS transaction ID against a device performing name resolution.

Affected Products

  • Zephyr RTOS v4.0.0
  • Zephyr RTOS versions between v4.0.0 and v4.4.0
  • Zephyr RTOS v4.4.0

Discovery Timeline

  • 2026-06-28 - CVE-2026-10646 published to NVD
  • 2026-06-29 - Last updated in NVD database

Technical Details for CVE-2026-10646

Vulnerability Analysis

The vulnerability lives in subsys/net/lib/sockets/getaddrinfo.c. The function allocates struct getaddrinfo_state ai_state on the stack and passes its address as user_data to dns_get_addr_info(). It then waits on ai_state->sem with a timeout set slightly longer than the resolver's own per-query timeout.

When the semaphore wait returns -EAGAIN, the pre-fix path executes goto again without cancelling the outstanding resolver query or resetting the semaphore. The prior DNS slot remains active with the stack pointer still bound as its callback context. Because ai_state->dns_id is overwritten by the retry, the stale query becomes uncancellable through the normal id-based API.

A later DNS response arriving over UDP and matched by its 16-bit transaction id inside dispatcher_cb()/dns_read(), or the resolver's own delayed query-timeout work, invokes dns_resolve_cb() against the now out-of-scope stack frame. The callback writes through the stale pointer into state->status, state->idx, state->ai_arr[], and calls k_sem_give().

Root Cause

The socket layer assumed the semaphore timeout would always follow a completed resolver callback. Workqueue contention and the documented configuration where CONFIG_NET_SOCKETS_DNS_TIMEOUT exceeds CONFIG_NET_SOCKETS_DNS_BACKOFF_INTERVAL invalidate that assumption. The retry path did not cancel the prior query or reset the semaphore, leaving a dangling stack reference in the resolver.

Attack Vector

DNS transaction IDs are 16 bits and can be brute-forced, spoofed, or replayed by an on- or off-path attacker who can observe or race legitimate queries. A response matching the stale id triggers the callback against reclaimed stack memory, causing crashes or memory corruption on the target device.

c
 	ret = dns_get_addr_info(host, qtype, &ai_state->dns_id,
 				dns_resolve_cb, ai_state, timeout_ms);
 	if (ret == 0) {
-		/* If the DNS query for reason fails so that the
-		 * dns_resolve_cb() would not be called, then we want the
-		 * semaphore to timeout so that we will not hang forever.
-		 * So make the sem timeout longer than the DNS timeout so that
-		 * we do not need to start to cancel any pending DNS queries.
+		/* If the resolver callback is not called for any reason, let the
+		 * semaphore timeout so getaddrinfo() does not hang forever.
+		 * Keep sem timeout slightly longer than the DNS timeout.
 		 */
 		ret = k_sem_take(&ai_state->sem, K_MSEC(timeout_ms + 100));
 		if (ret == -EAGAIN) {
+			(void) dns_cancel_addr_info_with_name(host, qtype, ai_state->dns_id);
+
 			if (!sys_timepoint_expired(end)) {
+				k_sem_reset(&ai_state->sem);
 				timeout = recalc_timeout(end, timeout);
 				goto again;
 			}

Source: Zephyr commit cd27da58

Detection Methods for CVE-2026-10646

Indicators of Compromise

  • Unexpected crashes, faults, or reboots on Zephyr devices during or shortly after hostname resolution operations.
  • Unsolicited DNS response packets arriving with transaction IDs that match recent outbound queries but from unexpected sources.
  • Repeated -EAGAIN returns from k_sem_take() in getaddrinfo() visible in device logs prior to a fault.

Detection Strategies

  • Enable Zephyr fault and stack-corruption tracing (CONFIG_STACK_SENTINEL, CONFIG_HW_STACK_PROTECTION) to catch writes to freed stack frames.
  • Inspect network captures for DNS responses whose source or timing does not match legitimate resolver traffic against embedded fleets.
  • Monitor firmware version inventories for devices still running Zephyr v4.0.0 through v4.4.0.

Monitoring Recommendations

  • Log and alert on repeated DNS resolution failures or timeouts from IoT devices, which may indicate resolver contention triggering the retry path.
  • Correlate device crash telemetry with DNS traffic anomalies at the network egress point.
  • Track upstream DNS servers used by Zephyr devices and restrict resolution to trusted resolvers where feasible.

How to Mitigate CVE-2026-10646

Immediate Actions Required

  • Upgrade Zephyr-based firmware to a release containing commit cd27da58eedb8d0fe380dd340b81ca5afa35de45 and rebuild affected images.
  • Audit product firmware inventories for Zephyr v4.0.0 through v4.4.0 and prioritize devices exposed to untrusted networks.
  • Restrict DNS traffic to trusted internal resolvers via network segmentation to reduce off-path spoofing opportunity.

Patch Information

The fix, published in the GitHub Security Advisory GHSA-h752-vhmf-29w6, introduces dns_cancel_addr_info_with_name() in include/zephyr/net/dns_resolve.h and calls it before retrying in getaddrinfo.c. It also resets the local semaphore with k_sem_reset() before goto again, eliminating the stale callback path.

Workarounds

  • Configure CONFIG_NET_SOCKETS_DNS_TIMEOUT to match CONFIG_NET_SOCKETS_DNS_BACKOFF_INTERVAL so the multi-retry path is not exercised.
  • Reduce workqueue contention that can delay resolver timeout work, lowering the chance of the -EAGAIN retry condition.
  • Enforce DNSSEC or DNS-over-TLS on upstream infrastructure to make transaction-id spoofing less practical for off-path attackers.
bash
# Configuration example: pin DNS timeout to backoff interval to avoid retry loop
CONFIG_NET_SOCKETS_DNS_TIMEOUT=2000
CONFIG_NET_SOCKETS_DNS_BACKOFF_INTERVAL=2000
CONFIG_STACK_SENTINEL=y
CONFIG_HW_STACK_PROTECTION=y

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.