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>
}
~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";