From e8d14939f9b5a7ac948ec6b8d5c1ac7f97e30151 Mon Sep 17 00:00:00 2001 From: Kenji Brameld Date: Tue, 19 May 2026 22:57:55 +0000 Subject: [PATCH] dlopen plugins with RTLD_NODELETE Plugin code is routinely referenced from heap-resident objects that outlive the dlopen/dlclose cycle (shared_ptr/weak_ptr control-block vtables, std::function captures). If dlclose actually unmaps the library, any later virtual call or callback through those references faults. Under gcc this is hidden because STB_GNU_UNIQUE symbols pin the .so; under clang there is no such pinning and the use-after-free crashes reliably. NODELETE makes the behavior consistent across compilers. Reproducer and full analysis: https://github.com/kbrameld/plugin_service_bug Signed-off-by: Kenji Brameld --- src/shared_library.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared_library.c b/src/shared_library.c index 077478f7..bd8e20fa 100644 --- a/src/shared_library.c +++ b/src/shared_library.c @@ -91,7 +91,10 @@ rcutils_load_shared_library( // for further reference. #ifndef _WIN32 - lib->lib_pointer = dlopen(library_path, RTLD_LAZY); + // RTLD_NODELETE: don't unmap on dlclose. Heap-resident references into the + // library (e.g. shared_ptr/weak_ptr control-block vtables) can outlive the + // dlopen/dlclose cycle; unmapping would dangle them. + lib->lib_pointer = dlopen(library_path, RTLD_LAZY | RTLD_NODELETE); if (NULL == lib->lib_pointer) { RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("dlopen error: %s", dlerror()); return RCUTILS_RET_ERROR;