Skip to content

Fix race condition in ctx_t::send_command during concurrent socket destruction - #4936

Open
Tyagiquamar wants to merge 2 commits into
zeromq:masterfrom
Tyagiquamar:fix-ctx-send-command-race
Open

Tyagiquamar wants to merge 2 commits into
zeromq:masterfrom
Tyagiquamar:fix-ctx-send-command-race

Conversation

@Tyagiquamar

Copy link
Copy Markdown

Problem

In \ctx_t::send_command(tid_, command_), _slots[tid_]\ is dereferenced to dispatch commands to mailboxes without holding _slot_sync.

Concurrently, when a socket is destroyed in \socket_base_t::check_destroy()\ -> \ctx_t::destroy_socket(), the reaper thread locks _slot_sync, clears _slots[tid] = NULL, and proceeds to delete the socket object and its embedded \mailbox_t.

When IO threads or peer sockets concurrently send routing commands (such as \ erm-ack, unbind, or pipe termination signals) to a socket undergoing destruction:

  1. If \send_command()\ reads _slots[tid_]\ before it is nulled but while \process_destroy()\ is freeing _mailbox, it invokes \signaler_t::send()\ on freed memory (ASan heap-use-after-free).
  2. If \send_command()\ reads _slots[tid_]\ after _slots[tid] = NULL, it causes a NULL pointer dereference (SIGSEGV).

Solution

  1. **Synchronize slot access in \ctx_t::send_command**:

    • Acquired _slot_sync\ in \send_command(tid_, command_)\ and added a null-pointer check \if (likely (slots[tid]))\ before invoking \send(). If the destination socket has already been destroyed, the orphaned command is safely dropped.
  2. Prevent recursive mutex deadlock during reaper termination:

    • In \destroy_socket(), determine whether _reaper->stop()\ is needed under _slot_sync\ and invoke _reaper->stop()\ after releasing _slot_sync.

Fixes #4926.

@Tyagiquamar

Copy link
Copy Markdown
Author

The failing Linux build passes a mailbox pointer to __builtin_expect through likely(slots[tid]). GCC requires an integer expression there. Changed it to likely(slots[tid] != NULL), preserving the slot lock and the null-slot behavior.

Reproduced the compiler error in Docker before the change. Ubuntu 24.04/GCC 13.3.0: library and three focused test binaries built; shutdown stress (1 case), context destruction/shutdown (9 cases), and endpoint termination (4 cases) passed. Clang-format 18.1.8 checked the changed function, and git diff --check passed. No full suite was run.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition in ctx_t::send_command() causes UAF during concurrent socket destruction

1 participant