fix cancel/accept race in NetAccept that causes EBADF abort - #13702
Open
JakeChampion wants to merge 4 commits into
Open
JakeChampion wants to merge 4 commits into
JakeChampion wants to merge 4 commits into
Conversation
`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`
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The listening-state check is not synchronized with cancellation, so the fatal accept-error race can still occur.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Fixes a cancellation/accept race that can produce EBADF and fatal accept errors.
Changes:
- Adds atomic listening-server state tracking.
- Applies checks across all three accept error paths.
- Updates cancellation and socket ownership handling.
File summaries
| File | Reviewed changes |
|---|---|
src/iocore/net/UnixNetAccept.cc |
Adds listening-state guards; cancellation remains unsynchronized with error dispatch, leaving the critical race unresolved. |
src/iocore/net/P_NetAccept.h |
Adds atomic server-state tracking and listening checks. |
Review details
Suppressed comments (2)
src/iocore/net/UnixNetAccept.cc:390
- The
is_listening()load is outside the mutex acquired on the next line. Cancellation can clear_server, close the socket, setcancelled, and release that mutex after this load but before this thread acquires it, so this branch can still deliverEVENT_ERRORto a cancelled continuation. Acquire the mutex before checkingis_listening()(or recheck after acquiring it) so the check and callback are serialized withcancel().
if (action_->is_listening()) {
src/iocore/net/UnixNetAccept.cc:583
- This path has no action/continuation mutex around the check or callback.
is_listening()is only an atomic pointer load, so it can return true, thencancel()can exchange and close the server and setcancelledbefore the next line executes;EVENT_ERRORis still delivered after cancellation. The guard and callback need to be serialized with cancellation (or use a cancellation state that reserves callback delivery).
if (action_->is_listening()) {
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| goto Ldone; | ||
| } | ||
| if (na->server.sock.is_ok() && !na->action_->cancelled) { | ||
| if (na->action_->is_listening()) { |
Contributor
There was a problem hiding this comment.
I think it's implied by the Action API that the caller MUST hold the action mutex when cancelling. If NetAccept::stop_accept does not, that may be another bug.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NetAcceptAction::cancel()closes the server socket before setting the cancelled flagIn the window between close and flag set,
net_accept()can getEBADFfromaccept4(), seecancelled=false, and dispatchEVENT_ERRORto handlers that don't expect itnow we check the atomic server pointer in all three accept paths before dispatching
EVENT_ERROR