From 28e40f32e6c56bc15fa7ca7e53c8436c002811b6 Mon Sep 17 00:00:00 2001 From: Jeremy Faivre Date: Thu, 15 Oct 2026 10:02:16 +0200 Subject: [PATCH] Release the keychain objects of the certificate lookup on macOS _hx_ssl_cert_load_defaults never released its search dictionary, the search list array nor the array returned by SecItemCopyMatching, which holds the whole system root store (about 440 KB per call). Every TLS context built from the default certificates leaked them. Found with the leaks tool on a macOS host making HTTPS requests. --- src/hx/libs/ssl/SSL.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/hx/libs/ssl/SSL.cpp b/src/hx/libs/ssl/SSL.cpp index f355042f3..485708933 100644 --- a/src/hx/libs/ssl/SSL.cpp +++ b/src/hx/libs/ssl/SSL.cpp @@ -557,13 +557,17 @@ Dynamic _hx_ssl_cert_load_defaults(){ if( SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain",&keychain) != errSecSuccess ) return null(); - // Search for certificates + // Search for certificates. Every CF object created or copied here is + // released: this runs for every TLS connection, and the matching array + // alone holds the whole system root store (hundreds of KB). search = CFDictionaryCreateMutable( NULL, 0, NULL, NULL ); + CFArrayRef searchList = CFArrayCreate(NULL, (const void **)&keychain, 1, NULL); CFDictionarySetValue( search, kSecClass, kSecClassCertificate ); CFDictionarySetValue( search, kSecMatchLimit, kSecMatchLimitAll ); CFDictionarySetValue( search, kSecReturnRef, kCFBooleanTrue ); - CFDictionarySetValue( search, kSecMatchSearchList, CFArrayCreate(NULL, (const void **)&keychain, 1, NULL) ); - if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess ){ + CFDictionarySetValue( search, kSecMatchSearchList, searchList ); + result = NULL; + if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess && result ){ CFIndex n = CFArrayGetCount( result ); for( CFIndex i = 0; i < n; i++ ){ item = (SecCertificateRef)CFArrayGetValueAtIndex( result, i ); @@ -580,6 +584,9 @@ Dynamic _hx_ssl_cert_load_defaults(){ } } } + if( result ) CFRelease( result ); + CFRelease( search ); + CFRelease( searchList ); CFRelease(keychain); if( chain != NULL ) return chain;