From 7720e6c43ec514dcf08a34d76ed122aaab0bbe06 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Sun, 19 Jan 2025 16:40:26 +0100 Subject: [PATCH] libutil: keep InterruptCallbacks state in shared_ptr destruction of static objects is not sequenced before termination of detached threads. this means that a detached thread can hold on to a reference to a static object and access that object after main() has returned and the static object destructor has run. ReceiveInterrupts is one such case, but moving the shared state to the heap solves it. Change-Id: Id597365ce9386000d171a03323169aafc542aa2c --- lix/libutil/signals.cc | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/lix/libutil/signals.cc b/lix/libutil/signals.cc index d32a03b76..1e9bf7b21 100644 --- a/lix/libutil/signals.cc +++ b/lix/libutil/signals.cc @@ -5,6 +5,7 @@ #include "lix/libutil/thread-name.hh" #include +#include #include namespace nix { @@ -46,7 +47,7 @@ struct InterruptCallbacks { std::map> callbacks; }; -static Sync _interruptCallbacks; +static Sync>> _interruptCallbacks; static void signalHandlerThread(sigset_t set) { @@ -68,12 +69,13 @@ void triggerInterrupt() { _isInterrupted = true; - { + auto callbacks = *_interruptCallbacks.lock(); + if (callbacks) { InterruptCallbacks::Token i = 0; while (true) { std::function callback; { - auto interruptCallbacks(_interruptCallbacks.lock()); + auto interruptCallbacks(callbacks->lock()); auto lb = interruptCallbacks->callbacks.lower_bound(i); if (lb == interruptCallbacks->callbacks.end()) break; @@ -167,24 +169,38 @@ void restoreSignals() /* RAII helper to automatically deregister a callback. */ struct InterruptCallbackImpl : InterruptCallback { + std::shared_ptr> parent; InterruptCallbacks::Token token; + InterruptCallbackImpl( + std::shared_ptr> parent, InterruptCallbacks::Token token + ) + : parent(parent) + , token(token) + { + } ~InterruptCallbackImpl() override { - auto interruptCallbacks(_interruptCallbacks.lock()); + auto interruptCallbacks(parent->lock()); interruptCallbacks->callbacks.erase(token); } }; std::unique_ptr createInterruptCallback(std::function callback) { - auto interruptCallbacks(_interruptCallbacks.lock()); + auto callbacks = *_interruptCallbacks.lock(); + if (!callbacks) { + auto lock = _interruptCallbacks.lock(); + if (!*lock) { + *lock = std::make_shared>(); + } + callbacks = *lock; + } + + auto interruptCallbacks(callbacks->lock()); auto token = interruptCallbacks->nextToken++; interruptCallbacks->callbacks.emplace(token, callback); - auto res = std::make_unique(); - res->token = token; - - return std::unique_ptr(res.release()); + return std::make_unique(callbacks, token); } };