Skip to content

Commit

Permalink
ares_destroy() race condition during shutdown due to missing lock (c…
Browse files Browse the repository at this point in the history
…-ares#831)

When using EventThreads, the config change cleanup code might manipulate
the event update list if it uses file descriptors (such as on Linux).
This was being done without a lock. Rework the event enqueuing to handle
locking internally to prevent this and to simplify where it is used.
This was found by chance during an ASAN CI run.

Fix By: Brad House (@bradh352)
  • Loading branch information
bradh352 authored Jul 24, 2024
1 parent b827cdc commit d4282d7
Showing 1 changed file with 34 additions and 24 deletions.
58 changes: 34 additions & 24 deletions src/lib/event/ares_event_thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ static void ares_event_destroy_cb(void *arg)
ares_free(event);
}

static void ares_event_signal(const ares_event_t *event)
{
if (event == NULL || event->signal_cb == NULL) {
return; /* LCOV_EXCL_LINE: DefensiveCoding */
}
event->signal_cb(event);
}

static void ares_event_thread_wake(const ares_event_thread_t *e)
{
if (e == NULL) {
return; /* LCOV_EXCL_LINE: DefensiveCoding */
}

ares_event_signal(e->ev_signal);
}

/* See if a pending update already exists. We don't want to enqueue multiple
* updates for the same event handle. Right now this is O(n) based on number
* of updates already enqueued. In the future, it might make sense to make
Expand Down Expand Up @@ -86,6 +103,7 @@ ares_status_t ares_event_update(ares_event_t **event, ares_event_thread_t *e,
ares_event_signal_cb_t signal_cb)
{
ares_event_t *ev = NULL;
ares_status_t status;

if (e == NULL) {
return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
Expand Down Expand Up @@ -116,18 +134,22 @@ ares_status_t ares_event_update(ares_event_t **event, ares_event_thread_t *e,

/* That's all the validation we can really do */

ares__thread_mutex_lock(e->mutex);

/* See if we have a queued update already */
ev = ares_event_update_find(e, fd, data);
if (ev == NULL) {
/* Allocate a new one */
ev = ares_malloc_zero(sizeof(*ev));
if (ev == NULL) {
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}

if (ares__llist_insert_last(e->ev_updates, ev) == NULL) {
ares_free(ev); /* LCOV_EXCL_LINE: OutOfMemory */
return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
ares_free(ev); /* LCOV_EXCL_LINE: OutOfMemory */
status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
goto done; /* LCOV_EXCL_LINE: OutOfMemory */
}
}

Expand All @@ -150,24 +172,17 @@ ares_status_t ares_event_update(ares_event_t **event, ares_event_thread_t *e,
*event = ev;
}

return ARES_SUCCESS;
}
status = ARES_SUCCESS;

static void ares_event_signal(const ares_event_t *event)
{
if (event == NULL || event->signal_cb == NULL) {
return; /* LCOV_EXCL_LINE: DefensiveCoding */
done:
if (status == ARES_SUCCESS) {
/* Wake event thread if successful so it can pull the updates */
ares_event_thread_wake(e);
}
event->signal_cb(event);
}

static void ares_event_thread_wake(const ares_event_thread_t *e)
{
if (e == NULL) {
return; /* LCOV_EXCL_LINE: DefensiveCoding */
}
ares__thread_mutex_unlock(e->mutex);

ares_event_signal(e->ev_signal);
return status;
}

static void ares_event_thread_process_fd(ares_event_thread_t *e,
Expand Down Expand Up @@ -195,15 +210,10 @@ static void ares_event_thread_sockstate_cb(void *data, ares_socket_t socket_fd,
flags |= ARES_EVENT_FLAG_WRITE;
}

/* Update channel fd */
ares__thread_mutex_lock(e->mutex);
/* Update channel fd. This function will lock e->mutex and also wake the
* event thread to process the update */
ares_event_update(NULL, e, flags, ares_event_thread_process_fd, socket_fd,
NULL, NULL, NULL);

/* Wake the event thread so it properly enqueues any updates */
ares_event_thread_wake(e);

ares__thread_mutex_unlock(e->mutex);
}

static void ares_event_process_updates(ares_event_thread_t *e)
Expand Down

0 comments on commit d4282d7

Please sign in to comment.