From e86e61b75b88adc0a4ef5a4d1bb88dc1f0f2926d Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sun, 26 Jan 2025 17:52:17 +0100 Subject: [PATCH] IXWebSocketTransport::setReadyState(): Run under lock When setReadyState(CLOSED) is executed from two different threads (the server's thread, detecting a close trying to receive and a separate sending thread), there is a race where both see _readyState as non-CLOSED and both execute the _onCloseCallback(). Worse, the server's thread might be returning from ->run(), unsetting the callback while the other thread is still about to call the _onCloseCallback(), resulting in a crash rather than "just" a duplicate invocation of the callback. This change ensures that setReadyState() *and* the_onCloseCallback() are executed by a single thread till completion, by introducing a new _setReadyStateMutex instance held during setReadyState() execution. --- ixwebsocket/IXWebSocketTransport.cpp | 6 ++++++ ixwebsocket/IXWebSocketTransport.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/ixwebsocket/IXWebSocketTransport.cpp b/ixwebsocket/IXWebSocketTransport.cpp index f9d36c52..23072f34 100644 --- a/ixwebsocket/IXWebSocketTransport.cpp +++ b/ixwebsocket/IXWebSocketTransport.cpp @@ -205,6 +205,12 @@ namespace ix void WebSocketTransport::setReadyState(ReadyState readyState) { + // Lock the _setReadyStateMutex for the duration of this + // method. This ensures that only a single thread runs the + // logic below avoiding concurrent execution of the + // close callback my multiple threads at the same time. + std::lock_guard lock(_setReadyStateMutex); + // No state change, return if (_readyState == readyState) return; diff --git a/ixwebsocket/IXWebSocketTransport.h b/ixwebsocket/IXWebSocketTransport.h index 8473c55c..97cfc15a 100644 --- a/ixwebsocket/IXWebSocketTransport.h +++ b/ixwebsocket/IXWebSocketTransport.h @@ -185,6 +185,8 @@ namespace ix // Hold the state of the connection (OPEN, CLOSED, etc...) std::atomic _readyState; + // Mutex to serialize setReadyState() execution. + std::mutex _setReadyStateMutex; OnCloseCallback _onCloseCallback; std::string _closeReason;