Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/iocore/net/UnixNetAccept.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ net_accept(NetAccept *na, void *ep, bool blockable)
if (res == -EAGAIN || res == -ECONNABORTED || res == -EPIPE) {
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.
if (!blockable) {
na->action_->continuation->handleEvent(EVENT_ERROR, reinterpret_cast<void *>(res));
} else {
Expand Down Expand Up @@ -387,7 +387,7 @@ NetAccept::do_blocking_accept(EThread *t)
case -1:
[[fallthrough]];
default:
if (!action_->cancelled) {
if (action_->server.load(std::memory_order_acquire) != nullptr && !action_->cancelled) {
SCOPED_MUTEX_LOCK(lock, action_->mutex ? action_->mutex : t->mutex, t);
action_->continuation->handleEvent(EVENT_ERROR, reinterpret_cast<void *>(res));
Warning("accept thread received fatal error: errno = %d", errno);
Expand Down Expand Up @@ -580,7 +580,7 @@ NetAccept::acceptFastEvent(int event, void *ep)
check_transient_accept_error(res);
goto Ldone;
}
if (!action_->cancelled) {
if (action_->server.load(std::memory_order_acquire) != nullptr && !action_->cancelled) {
action_->continuation->handleEvent(EVENT_ERROR, reinterpret_cast<void *>(res));
}
goto Lerror;
Expand Down