From 3928517d3755fe5cff303f8098ebc2e3648161cd Mon Sep 17 00:00:00 2001 From: Tolya Korniltsev Date: Tue, 14 Apr 2026 11:51:35 +1000 Subject: [PATCH] fix: make ProfilerSignalManager::_handler atomic to prevent data race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _handler is written on a normal thread in RegisterHandler and read inside a signal handler in CallCustomHandler. Without atomics this is undefined behavior — the C++ standard requires lock-free atomics for signal-safe access ([support.signal]/3). Also fixes the double-checked locking in RegisterHandler: the second check after acquiring the mutex was comparing a stale local copy instead of re-loading from the (now atomic) _handler. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ProfilerSignalManager.cpp | 9 +++++---- .../ProfilerSignalManager.h | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.cpp index 3f4fd1feb982..cb9e61fb24e8 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.cpp @@ -28,7 +28,7 @@ ProfilerSignalManager::~ProfilerSignalManager() noexcept _isHandlerInPlace = false; sigaction(_signalToSend, &_previousAction, nullptr); } - _handler = nullptr; + _handler.store(nullptr, std::memory_order_relaxed); } ProfilerSignalManager* ProfilerSignalManager::Get(int signal) @@ -49,7 +49,7 @@ ProfilerSignalManager* ProfilerSignalManager::Get(int signal) bool ProfilerSignalManager::RegisterHandler(HandlerFn_t handler) { - HandlerFn_t current = _handler; + HandlerFn_t current = _handler.load(std::memory_order_acquire); if (current != nullptr) { @@ -58,6 +58,7 @@ bool ProfilerSignalManager::RegisterHandler(HandlerFn_t handler) } std::unique_lock lock(_handlerRegisterMutex); + current = _handler.load(std::memory_order_acquire); if (current != nullptr) { assert(current == handler); @@ -68,7 +69,7 @@ bool ProfilerSignalManager::RegisterHandler(HandlerFn_t handler) if (_isHandlerInPlace) { - _handler = handler; + _handler.store(handler, std::memory_order_release); } return _isHandlerInPlace; @@ -204,7 +205,7 @@ void ProfilerSignalManager::SignalHandler(int signal, siginfo_t* info, void* con bool ProfilerSignalManager::CallCustomHandler(int32_t signal, siginfo_t* info, void* context) { - HandlerFn_t handler = _handler; + HandlerFn_t handler = _handler.load(std::memory_order_acquire); return handler != nullptr && handler(signal, info, context); } diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.h index a4f4dcc50b62..8593e616ca90 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/ProfilerSignalManager.h @@ -27,7 +27,7 @@ class ProfilerSignalManager #ifdef DD_TEST void Reset() { - _handler = nullptr; + _handler.store(nullptr, std::memory_order_relaxed); sigaction(_signalToSend, &_previousAction, nullptr); _isHandlerInPlace = false; _canReplaceSignalHandler = true; @@ -56,7 +56,7 @@ class ProfilerSignalManager private: bool _canReplaceSignalHandler; int32_t _signalToSend; - HandlerFn_t _handler; + std::atomic _handler; pid_t _processId; bool _isHandlerInPlace; struct sigaction _previousAction;