Skip to content

Release the keychain objects of the certificate lookup on macOS (fix memory leak) - #1424

Open
jeremyfa wants to merge 1 commit into
HaxeFoundation:masterfrom
ceramic-engine:fix/macos-keychain-release
Open

jeremyfa wants to merge 1 commit into
HaxeFoundation:masterfrom
ceramic-engine:fix/macos-keychain-release

Conversation

@jeremyfa

Copy link
Copy Markdown
Contributor

_hx_ssl_cert_load_defaults (src/hx/libs/ssl/SSL.cpp, macOS) creates
or copies three Core Foundation objects it never releases:

  • search, the query dictionary (CFDictionaryCreateMutable);
  • the keychain search list (CFArrayCreate), built inline in the
    CFDictionarySetValue call, so nothing even holds a reference to it;
  • result, the array returned by SecItemCopyMatching, which holds every
    root certificate of the system keychain (about 440 KB, 1 176 items here).

Only dat and keychain were released. Every TLS context built from the
default certificates therefore leaked the whole root store.

Found with the leaks tool on a macOS host making HTTPS requests through
sys.ssl.Socket: after the fix the same run reports zero leaked blocks.
The patch keeps the search list in a variable, releases the three objects
after the loop, and also guards against a null result.

(Disclaimer: found this using Claude Fable 5.1)

_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.
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
_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.

From HaxeFoundation/hxcpp#1424.

Fixes #160

@tobil4sk tobil4sk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable, thanks! I guess after this patch there are no more leaks reported?

Comment thread src/hx/libs/ssl/SSL.cpp
CFDictionarySetValue( search, kSecMatchSearchList, CFArrayCreate(NULL, (const void **)&keychain, 1, NULL) );
if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess ){
CFDictionarySetValue( search, kSecMatchSearchList, searchList );
result = NULL;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be initialised to NULL at the declaration?

Comment thread src/hx/libs/ssl/SSL.cpp
Comment on lines +560 to +562
// 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).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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 for certificates
// Make sure to release anything we allocate

I think it is self explanatory why we should not leak objects, so a short reminder should be enough

Comment thread src/hx/libs/ssl/SSL.cpp
if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess ){
CFDictionarySetValue( search, kSecMatchSearchList, searchList );
result = NULL;
if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess && result ){

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any case where it would return errSecSuccess but give a null result?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about that, but I'd say it doesn't harm testing it

@jeremyfa

Copy link
Copy Markdown
Contributor Author

Looks reasonable, thanks! I guess after this patch there are no more leaks reported?

I indeed didn't find another leak after that one was patched!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants