Repository navigation
[Backport] net: sockets: getaddrinfo() retry after a DNS timeout leaves the previous query in flight and touches stale stack state #110771
Description
Describe the bug
zsock_getaddrinfo() (subsys/net/lib/sockets/getaddrinfo.c) resolves a
hostname through a stack-allocated state object and hands a pointer to it to
the asynchronous DNS resolver as the callback user_data:
struct getaddrinfo_state ai_state; /* automatic (stack) variable */
...
dns_get_addr_info(host, qtype, &ai_state->dns_id,
dns_resolve_cb, ai_state, timeout_ms);It then waits on a semaphore whose timeout is deliberately set slightly longer
than the resolver's own per-query timeout, and retries on -EAGAIN:
ret = k_sem_take(&ai_state->sem, K_MSEC(timeout_ms + 100));
if (ret == -EAGAIN) {
if (!sys_timepoint_expired(end)) {
timeout = recalc_timeout(end, timeout);
goto again; /* retry — previous query left active */
}
(void)dns_cancel_addr_info(ai_state->dns_id);
st = DNS_EAI_AGAIN;
}On the -EAGAIN retry path the code jumps back to again: and issues a fresh
query without cancelling the previous, still-active one and without resetting
the semaphore. The next iteration overwrites ai_state->dns_id, so the old
query's id is lost and can no longer be cancelled even on the final give-up. The
old resolver slot still holds cb = dns_resolve_cb and user_data = &ai_state —
a live pointer into the caller's stack frame.
The resolver delivers responses asynchronously from the network RX path, matching
a response to a slot purely by its 16-bit transaction id. When a late or
duplicated DNS response (or the resolver's own delayed timeout work) finally fires
that stale callback, dns_resolve_cb() writes through the state pointer —
state->status, state->ai_arr[state->idx], and k_sem_give(&state->sem) — into
a stack frame that getaddrinfo() has already returned from or reused on a later
retry. The stray k_sem_give() operates on a semaphore whose backing memory is
gone, corrupting kernel wait-list state and crashing in the scheduler /
sys_dlist_remove path on the system workqueue.
This is reachable whenever a DNS server is slow, lossy, or unavailable so that the
socket-layer semaphore wait times out before the resolver tears the slot down, and
the window widens when CONFIG_NET_SOCKETS_DNS_TIMEOUT > CONFIG_NET_SOCKETS_DNS_BACKOFF_INTERVAL configures multiple retry attempts.
Expected behavior
Before retrying after a k_sem_take() timeout, getaddrinfo() should cancel the
timed-out query and reset the semaphore, so no delayed callback can reach the
stack-backed state after the frame has been reused. The cancellation should be
qualified by name and query type (not id alone) so that a resolver-assigned id-0
query (e.g. mDNS) is released without accidentally cancelling an unrelated query
that also uses id 0.
Affected code
subsys/net/lib/sockets/getaddrinfo.c — the -EAGAIN retry path in
z_impl_z_zsock_getaddrinfo_internal(). The name-qualified cancellation it needs
relies on dns_resolve_cancel_with_name() in subsys/net/lib/dns/resolve.c
completing reliably on the timeout path: its temporary packed-name buffer
allocation must not fail when the context buffer timeout has already expired (it
should allocate with K_FOREVER), or the cancel can silently no-op exactly when
it is needed.
Activity
[-]net: sockets: getaddrinfo() retry after a DNS timeout leaves the previous query in flight and touches stale stack state[/-][+][Backport] net: sockets: getaddrinfo() retry after a DNS timeout leaves the previous query in flight and touches stale stack state[/+]carlescufi commentedon Jul 1, 2026
@d3zd3z this may be missing a backport to v3.7?
github-actions commentedon Aug 1, 2026
This issue has been marked as stale because it has been open (more than) 30 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 7 days. Note, that you can always re-open a closed issue at any time.