]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-dencoder: skip dlclose under ASan so leaks symbolise 68856/head
authorKefu Chai <k.chai@proxmox.com>
Sat, 25 Apr 2026 03:54:16 +0000 (11:54 +0800)
committerKefu Chai <k.chai@proxmox.com>
Thu, 4 Jun 2026 06:02:05 +0000 (14:02 +0800)
The DencoderPlugin destructor calls dlclose() on each loaded plugin .so
before main() returns.  When LeakSanitizer's atexit-registered leak
check then runs, the plugin .so is no longer mapped in the process
address space, and any leak whose allocation stack passes through
plugin code is reported with "<unknown module>" — useless for
investigation.

This is not an ASan bug or a missing -g flag; it is the unavoidable
ordering of dlclose-before-leak-check.  Skipping dlclose under
__SANITIZE_ADDRESS__ leaves the plugins mapped until process exit,
restoring symbolisation.  The functional cost is zero: the kernel will
unmap the .so segments when the process exits anyway.

Without this, a developer triaging dencoder leaks under ASan has no
visibility into which plugin or which static initialiser is
responsible.  With this, the leak stacks resolve to real symbols and
real bugs become triagable.

This commit does not change leak-detection results in non-sanitiser
builds and is a no-op on FreeBSD (which already skipped dlclose).

Signed-off-by: Kefu Chai <k.chai@proxmox.com>
src/tools/ceph-dencoder/denc_plugin.h

index c5eacce47cb43bfe05749d554b422cb581d1c0de..09cb1438f17c8aabe39c843c62bbd9a788e7f2f5 100644 (file)
@@ -24,11 +24,23 @@ public:
   }
   ~DencoderPlugin() {
     unregister_dencoders();
-#if !defined(__FreeBSD__)
+#if defined(__has_feature)
+#  if __has_feature(address_sanitizer) || __has_feature(leak_sanitizer)
+#    define DENC_SKIP_DLCLOSE 1
+#  endif
+#endif
+#if defined(__SANITIZE_ADDRESS__) && !defined(DENC_SKIP_DLCLOSE)
+#  define DENC_SKIP_DLCLOSE 1
+#endif
+#if !defined(__FreeBSD__) && !defined(DENC_SKIP_DLCLOSE)
+    // Skip dlclose under ASan/LSan: the leak checker at process exit needs
+    // the .so still mapped to resolve symbols. Clang may not define
+    // __SANITIZE_ADDRESS__ (e.g. clang-19), hence the __has_feature check.
     if (mod) {
       dlclose(mod);
     }
 #endif
+#undef DENC_SKIP_DLCLOSE
   }
   const dencoders_t& register_dencoders() {
     static constexpr std::string_view REGISTER_DENCODERS_FUNCTION = "register_dencoders\0";