Skip to content

Commit 2d0b259

Browse files
committed
Fix NetAcceptAction::cancel() use-after-free race condition
Fix a race condition between NetAcceptAction::cancel() and NetAccept::acceptEvent() where the server pointer could be dereferenced after the NetAccept object was deleted. The race occurred as follows: 1. Thread T4 calls NetAcceptAction::cancel(), sets cancelled=true 2. Thread T3 running acceptEvent() sees cancelled==true 3. Thread T3 deletes the NetAccept (including embedded Server) 4. Thread T4 tries to call server->close() on freed memory The fix uses std::atomic<Server*> with atomic exchange to ensure only one thread can successfully obtain and use the server pointer. Both cancel() and the cleanup paths before delete this atomically exchange the pointer with nullptr - whichever succeeds first closes the server, the other becomes a no-op. This addresses the TODO comment that was in the code: "// TODO fix race between cancel accept and call back" ASAN report this fixes (seen intermittently on rocky CI builds): ==8850==ERROR: AddressSanitizer: heap-use-after-free on address 0x616000028cb4 at pc 0x000001346739 bp 0x7fa40fd2f580 sp 0x7fa40fd2f570 WRITE of size 4 at 0x616000028cb4 thread T4 ([ET_NET 2]) #0 0x1346738 in UnixSocket::close() ../src/iocore/eventsystem/UnixSocket.cc:138 #1 0x12b44ed in Server::close() ../src/iocore/net/Server.cc:88 #2 0x121fb95 in NetAcceptAction::cancel(Continuation*) ../src/iocore/net/P_NetAccept.h:71 #3 0x7fa41686d082 in TSActionCancel(tsapi_action*) ../src/api/InkAPI.cc:5828 ... 0x616000028cb4 is located 308 bytes inside of 576-byte region [0x616000028b80,0x616000028dc0) freed by thread T3 ([ET_NET 1]) here: #0 0x7fa416d2036f in operator delete(void*, unsigned long) #1 0x12593c4 in NetAccept::~NetAccept() ../src/iocore/net/P_NetAccept.h:128 #2 0x12bebf0 in NetAccept::acceptEvent(int, void*) ../src/iocore/net/UnixNetAccept.cc:484 ...
1 parent 05f06fa commit 2d0b259

4 files changed

Lines changed: 26 additions & 6 deletions

File tree

src/iocore/net/P_NetAccept.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "iocore/net/NetAcceptEventIO.h"
4444
#include "Server.h"
4545

46+
#include <atomic>
4647
#include <vector>
4748

4849
struct NetAccept;
@@ -60,15 +61,17 @@ AcceptFunction net_accept;
6061

6162
class UnixNetVConnection;
6263

63-
// TODO fix race between cancel accept and call back
6464
struct NetAcceptAction : public Action, public RefCountObjInHeap {
65-
Server *server;
65+
std::atomic<Server *> server{nullptr};
6666

6767
void
6868
cancel(Continuation *cont = nullptr) override
6969
{
7070
Action::cancel(cont);
71-
server->close();
71+
Server *s = server.exchange(nullptr, std::memory_order_acq_rel);
72+
if (s != nullptr) {
73+
s->close();
74+
}
7275
}
7376

7477
Continuation *

src/iocore/net/QUICNetProcessor.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ QUICNetProcessor::main_accept(Continuation *cont, SOCKET fd, AcceptOptions const
253253

254254
na->action_ = new NetAcceptAction();
255255
*na->action_ = cont;
256-
na->action_->server = &na->server;
256+
na->action_->server.store(&na->server, std::memory_order_release);
257257
na->init_accept();
258258

259259
return na->action_.get();

src/iocore/net/UnixNetAccept.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,13 +480,21 @@ NetAccept::acceptEvent(int event, void *ep)
480480
if (lock.is_locked()) {
481481
if (action_->cancelled) {
482482
e->cancel();
483+
Server *s = action_->server.exchange(nullptr, std::memory_order_acq_rel);
484+
if (s != nullptr) {
485+
s->close();
486+
}
483487
Metrics::Gauge::decrement(net_rsb.accepts_currently_open);
484488
delete this;
485489
return EVENT_DONE;
486490
}
487491

488492
int res;
489493
if ((res = net_accept(this, e, false)) < 0) {
494+
Server *s = action_->server.exchange(nullptr, std::memory_order_acq_rel);
495+
if (s != nullptr) {
496+
s->close();
497+
}
490498
Metrics::Gauge::decrement(net_rsb.accepts_currently_open);
491499
/* INKqa11179 */
492500
Warning("Accept on port %d failed with error no %d", ats_ip_port_host_order(&server.addr), res);
@@ -637,7 +645,12 @@ NetAccept::acceptFastEvent(int event, void *ep)
637645
return EVENT_CONT;
638646

639647
Lerror:
640-
server.close();
648+
{
649+
Server *s = action_->server.exchange(nullptr, std::memory_order_acq_rel);
650+
if (s != nullptr) {
651+
s->close();
652+
}
653+
}
641654
e->cancel();
642655
Metrics::Gauge::decrement(net_rsb.accepts_currently_open);
643656
delete this;
@@ -656,6 +669,10 @@ NetAccept::acceptLoopEvent(int event, Event *e)
656669
}
657670

658671
// Don't think this ever happens ...
672+
Server *s = action_->server.exchange(nullptr, std::memory_order_acq_rel);
673+
if (s != nullptr) {
674+
s->close();
675+
}
659676
Metrics::Gauge::decrement(net_rsb.accepts_currently_open);
660677
delete this;
661678
return EVENT_DONE;

src/iocore/net/UnixNetProcessor.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ UnixNetProcessor::accept_internal(Continuation *cont, int fd, AcceptOptions cons
135135

136136
na->action_ = new NetAcceptAction();
137137
*na->action_ = cont;
138-
na->action_->server = &na->server;
138+
na->action_->server.store(&na->server, std::memory_order_release);
139139

140140
if (opt.frequent_accept) { // true
141141
if (accept_threads > 0 && listen_per_thread == 0) {

0 commit comments

Comments
 (0)