Skip to content

dnsthread: register the wakeup listener before starting work - #439

Open
woahwhattheheck wants to merge 1 commit into
Tarsnap:masterfrom
woahwhattheheck:dnsthread-register-before-signal
Open

dnsthread: register the wakeup listener before starting work#439
woahwhattheheck wants to merge 1 commit into
Tarsnap:masterfrom
woahwhattheheck:dnsthread-register-before-signal

Conversation

@woahwhattheheck

@woahwhattheheck woahwhattheheck commented Sep 6, 2026

Copy link
Copy Markdown

Disclosure, per this repository's AGENTS.md: I am an LLM (Claude), submitting on behalf of the account owner. I am available to discuss this change and to revise it in response to review feedback.

Fixes #438.

dnsthread_resolveone() set THREAD_HASWORK and signalled the worker thread
before registering the wakeup listener, and err1 only unlocked the mutex.
A failed events_network_register() therefore returned -1 with a resolution
already running and nothing registered to read its completion byte: T->addr
and T->sas both leaked, the caller's callback never fired, and the
undrained byte made the next dnsthread_resolveone() fire
callback_resolveone() immediately — freeing T->addr while the worker
thread was passing that same pointer to sock_resolve().

The change

Register the listener before handing the work over, and unwind properly:

  • err2 frees the strduped address — reached when the registration fails,
    at which point the worker has not been touched.
  • err3 additionally restores THREAD_SLEEPING and cancels the
    registration — reached only if pthread_cond_signal() fails.
  • err1 is unchanged and still handles the strdup() failure, where there is
    nothing to free.

events_network_cancel()'s return is ignored, matching network_accept.c,
network_connect.c, network_read.c and network_write.c.

Note

I did not try to make the failure path take the work back from the worker
once it is running, because it cannot be done safely — the worker drops the
mutex for the duration of sock_resolve(). Doing the registration first
means that situation never arises.

dnsthread_resolveone() set THREAD_HASWORK and signalled the worker thread
before registering the wakeup listener, and err1 only unlocked the mutex.

A failed events_network_register() therefore returned -1 with a resolution
already running and nothing registered to read its completion byte.
callback_resolveone() is the only thing which frees T->addr and the only
consumer of T->sas, so both leaked, and the caller's callback never fired.

The undrained byte then made the next dnsthread_resolveone() on the same
thread fire callback_resolveone() immediately, which freed T->addr while
the worker was passing that same pointer to sock_resolve(); the worker
drops the mutex for the duration of the resolution, so the lock does not
prevent this.  That callback also handed the previous resolution's result
to the new request.

Register the listener before handing the work over, so that a failure
leaves the worker untouched and the address can simply be freed.  err2
frees the address; err3 additionally restores THREAD_SLEEPING and cancels
the registration, and is reached only if pthread_cond_signal() fails.
err1 still handles the strdup() failure, where there is nothing to free.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[bug bounty] dnsthread_resolveone() abandons a running resolution if the wakeup listener fails to register

2 participants