From 883eb2b1ef5dbae1526e33204fa18a2eb06bac36 Mon Sep 17 00:00:00 2001 From: Jahnvi Thakkar Date: Fri, 19 Jun 2026 11:51:20 +0530 Subject: [PATCH] Clear unixODBC ini cache at module shutdown (GH#1660) AddressSanitizer leak reports were traced to libodbcinst's internal ini cache created by SQLGetPrivateProfileString in token_cache_init_ttl. Call __clear_ini_cache() during core_sqlsrv_mshutdown on Unix when the symbol is available, using dlopen(RTLD_NOLOAD)+dlsym to avoid hard linking and preserve compatibility across unixODBC variants. --- source/shared/core_init.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/source/shared/core_init.cpp b/source/shared/core_init.cpp index 4f79f26bc..248f3624d 100644 --- a/source/shared/core_init.cpp +++ b/source/shared/core_init.cpp @@ -19,6 +19,10 @@ #include "core_sqlsrv.h" +#ifndef _WIN32 +#include +#endif + // module global variables (initialized in minit and freed in mshutdown) HMODULE g_sqlsrv_hmodule = NULL; bool isVistaOrGreater; @@ -159,6 +163,23 @@ void core_sqlsrv_mshutdown( _Inout_ sqlsrv_context& henv_cp, _Inout_ sqlsrv_cont } delete &henv_cp; +#ifndef _WIN32 + // Release the ini file cache allocated by libodbcinst when we called + // SQLGetPrivateProfileString during MINIT (token_cache_init_ttl). + // The ODBC installer library caches parsed ini data internally and + // never frees it, which is reported as a leak by AddressSanitizer. + // __clear_ini_cache is a unixODBC-specific export, so we load it + // dynamically and call it only if available. + void* hInst = dlopen( "libodbcinst.so.2", RTLD_NOW | RTLD_NOLOAD ); + if( hInst ) { + void (*clear_cache)(void) = reinterpret_cast( dlsym( hInst, "__clear_ini_cache" ) ); + if( clear_cache ) { + clear_cache(); + } + dlclose( hInst ); + } +#endif + return; }