From 8a035df5c872321b7c10363109913b0b7e25535a Mon Sep 17 00:00:00 2001 From: Gustavo Lima Chaves Date: Fri, 14 Aug 2026 17:11:11 -0700 Subject: [PATCH] Fix X509 reference leak in SslStream extra chain certificates CryptoNative_SslAddExtraChainCert uses SSL_ctrl(ssl, SSL_CTRL_CHAIN_CERT, 1, x509), which is SSL_add1_chain_cert and takes its own reference. Its sibling CryptoNative_SslCtxAddExtraChainCert uses SSL_CTX_add_extra_chain_cert, which is add0 and takes ownership of the caller's reference. Both are driven by managed helpers that are character for character identical (Interop.Ssl.cs:312 and Interop.SslCtx.cs:47): they up-ref with Crypto.X509UpRef and then call SetHandleAsInvalid to transfer ownership. That is the add0 contract, so against add1 two references are taken and neither is handed back. SetHandleAsInvalid also disarms the SafeHandle, so no finalizer reclaims it: the native X509, its X509_PUBKEY and the EVP_PKEY cached inside it survive until process exit, invisible to the GC and to a managed heap dump. Fixes #132350 --- .../libs/System.Security.Cryptography.Native/pal_ssl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c index 811e2cc2a64d9d..d94c4516944483 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c @@ -1053,7 +1053,13 @@ int32_t CryptoNative_SslAddExtraChainCert(SSL* ssl, X509* x509) return 0; } - if (SSL_ctrl(ssl, SSL_CTRL_CHAIN_CERT, 1,(void*)x509) == 1) + // larg must be 0 (SSL_add0_chain_cert), not 1 (SSL_add1_chain_cert). The caller, + // Interop.Ssl.AddExtraChainCertificates, up-refs and then calls SetHandleAsInvalid to + // hand its reference over, which is the add0 contract and matches + // CryptoNative_SslCtxAddExtraChainCert above. With add1 libssl takes a reference of its + // own and the caller's is abandoned rather than released, leaking one X509 per + // intermediate per SSL handle. + if (SSL_ctrl(ssl, SSL_CTRL_CHAIN_CERT, 0,(void*)x509) == 1) { return 1; }