From 599f3b8d7b12829933c7fe05c33f70e428808cfd Mon Sep 17 00:00:00 2001 From: fishitiny Date: Wed, 5 Aug 2026 18:14:31 +0800 Subject: [PATCH] fix: prevent double-shutdown in rclcpp::Context The signal handler thread (deferred_signal_handler) and the main thread may both call Context::shutdown() during Ctrl-C, causing a double-free or heap corruption in the underlying rcl_context_t and glibc. Add an atomic flag is_shutting_down_ to ensure only the first call to shutdown() proceeds; subsequent calls return immediately. Steps to reproduce: 1. Launch any ROS 2 node with shutdown_on_signal=true 2. Press Ctrl-C 3. Observe SIGABRT in rclcpp::Context::shutdown() called from rclcpp::SignalHandler::deferred_signal_handler() GDB backtrace from core dump: Thread 1: raise() -> abort() -> rclcpp::Context::shutdown() -> rclcpp::SignalHandler::deferred_signal_handler() Signed-off-by: fishitiny --- rclcpp/include/rclcpp/context.hpp | 2 ++ rclcpp/src/rclcpp/context.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/rclcpp/include/rclcpp/context.hpp b/rclcpp/include/rclcpp/context.hpp index 1251e58b62..cb7786579d 100644 --- a/rclcpp/include/rclcpp/context.hpp +++ b/rclcpp/include/rclcpp/context.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -365,6 +366,7 @@ class Context : public std::enable_shared_from_this std::shared_ptr rcl_context_; rclcpp::InitOptions init_options_; std::string shutdown_reason_; + std::atomic is_shutting_down_{false}; // Keep shared ownership of the global logging mutex. std::shared_ptr logging_mutex_; diff --git a/rclcpp/src/rclcpp/context.cpp b/rclcpp/src/rclcpp/context.cpp index 33bd0bf0b9..7db3b1e3f2 100644 --- a/rclcpp/src/rclcpp/context.cpp +++ b/rclcpp/src/rclcpp/context.cpp @@ -299,6 +299,12 @@ Context::shutdown_reason() const bool Context::shutdown(const std::string & reason) { + // Prevent double-shutdown: the signal handler thread and the main thread + // may both call shutdown() during Ctrl-C. Use an atomic flag to ensure + // only the first call proceeds; subsequent calls return immediately. + if (is_shutting_down_.exchange(true)) { + return false; + } // prevent races std::lock_guard init_lock(init_mutex_); // ensure validity