Skip to content

[Backport] net: sockets: getaddrinfo() retry after a DNS timeout leaves the previous query in flight and touches stale stack state #110771

Closed as not planned

Description

@d3zd3z

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

  1. changed the title [-]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[/+] on Jul 1, 2026
  2. carlescufi commented on Jul 1, 2026

    @carlescufi
    Member

    @d3zd3z this may be missing a backport to v3.7?

  3. github-actions commented on Aug 1, 2026

    @github-actions

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions