Skip to content

fix cancel/accept race in NetAccept that causes EBADF abort#12925

Draft
JakeChampion wants to merge 1 commit intoapache:masterfrom
JakeChampion:jake/ccc
Draft

fix cancel/accept race in NetAccept that causes EBADF abort#12925
JakeChampion wants to merge 1 commit intoapache:masterfrom
JakeChampion:jake/ccc

Conversation

@JakeChampion
Copy link
Contributor

NetAcceptAction::cancel() closes the server socket before setting the cancelled flag In the window between close and flag set, net_accept() can get EBADF from accept4(), see cancelled=false, and dispatch EVENT_ERROR to handlers that don't expect it

now we check the atomic server pointer in all three accept paths before dispatching EVENT_ERROR

@JakeChampion JakeChampion force-pushed the jake/ccc branch 3 times, most recently from 27c9a8a to b259ce1 Compare February 27, 2026 14:43
`NetAcceptAction::cancel()` closes the server socket before setting the cancelled flag
In the window between close and flag set, `net_accept()` can get `EBADF` from `accept4()`, see `cancelled=false`, and dispatch
`EVENT_ERROR` to handlers that don't expect it

now we check the atomic server pointer in all three accept paths before dispatching `EVENT_ERROR`
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a race condition between NetAcceptAction::cancel() and the net_accept() accept paths that could cause an unhandled EBADF abort. The cancel operation atomically closes the server socket (via server.exchange(nullptr, ...)) before setting the cancelled flag, leaving a window where an accept call can fail with EBADF, see cancelled == false, and spuriously dispatch EVENT_ERROR to a continuation that does not expect it.

Changes:

  • Adds an atomic std::atomic<Server *> server check (action_->server.load(std::memory_order_acquire) != nullptr) as the primary guard before dispatching EVENT_ERROR in all three accept code paths (net_accept, do_blocking_accept, acceptFastEvent).
  • The NetAcceptAction::cancel() implementation (in P_NetAccept.h) uses server.exchange(nullptr, std::memory_order_acq_rel) so the null pointer state serves as a reliable cancellation indicator before cancelled is set.

goto Ldone;
}
if (na->server.sock.is_ok() && !na->action_->cancelled) {
if (na->action_->server.load(std::memory_order_acquire) != nullptr && na->server.sock.is_ok() && !na->action_->cancelled) {
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In net_accept, the updated condition at line 115 adds na->action_->server.load(std::memory_order_acquire) != nullptr as the primary atomic guard, but retains the pre-existing na->server.sock.is_ok() check. This is now redundant: when action_->cancel() runs, it first atomically exchanges server to nullptr, then closes the socket. So if the atomic load returns nullptr, short-circuit evaluation means sock.is_ok() is never reached. If the atomic load returns non-null, the socket is guaranteed to still be open (cancel hasn't run yet).

The other two fixed paths (do_blocking_accept and acceptFastEvent) correctly use only the atomic server pointer check, without an additional sock.is_ok() check, which is the right and consistent approach.

The na->server.sock.is_ok() sub-expression also performs a non-atomic read of the socket's file descriptor, which could be written concurrently by another thread calling cancel(). While in practice this doesn't create a real correctness problem (the atomic check is authoritative), it is an unnecessary TOCTOU discrepancy between the three accept paths.

Suggested change
if (na->action_->server.load(std::memory_order_acquire) != nullptr && na->server.sock.is_ok() && !na->action_->cancelled) {
if (na->action_->server.load(std::memory_order_acquire) != nullptr && !na->action_->cancelled) {

Copilot uses AI. Check for mistakes.
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.

3 participants