This is an automated email from the ASF dual-hosted git repository.

moonchen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 07aa06c79b TLS: Fix EVP_PKEY leak in SSLPrivateKeyHandler (#13464)
07aa06c79b is described below

commit 07aa06c79b829faa570acb52b453b3c0f26cff18
Author: Mo Chen <[email protected]>
AuthorDate: Fri Jul 31 14:16:00 2026 -0500

    TLS: Fix EVP_PKEY leak in SSLPrivateKeyHandler (#13464)
    
    SSL_CTX_use_PrivateKey() takes its own reference on the key, so the
    reference from PEM_read_bio_PrivateKey() belongs to the caller.
    SSLPrivateKeyHandler() released it only when attaching the key failed, so
    a successful load leaked one EVP_PKEY per certificate.  This repeats at
    startup, on every config reload, and on every secret or certificate
    update.  Hold the key in a scoped_PKEY so every exit releases it.
---
 src/iocore/net/P_SSLUtils.h |  9 +++++++++
 src/iocore/net/SSLUtils.cc  | 15 ++++++++-------
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/src/iocore/net/P_SSLUtils.h b/src/iocore/net/P_SSLUtils.h
index af5728e486..4fb3a39363 100644
--- a/src/iocore/net/P_SSLUtils.h
+++ b/src/iocore/net/P_SSLUtils.h
@@ -114,6 +114,14 @@ namespace detail
     }
   };
 
+  struct PKEYDeleter {
+    void
+    operator()(EVP_PKEY *p)
+    {
+      EVP_PKEY_free(p);
+    }
+  };
+
 #ifdef OPENSSL_IS_OPENSSL3
   struct PKEYCTXDeleter {
     void
@@ -156,6 +164,7 @@ private:
 
 using scoped_X509 = std::unique_ptr<X509, ssl::detail::X509Deleter>;
 using scoped_BIO  = std::unique_ptr<BIO, ssl::detail::BIODeleter>;
+using scoped_PKEY = std::unique_ptr<EVP_PKEY, ssl::detail::PKEYDeleter>;
 #ifdef OPENSSL_IS_OPENSSL3
 using scoped_PKEY_CTX    = std::unique_ptr<EVP_PKEY_CTX, 
ssl::detail::PKEYCTXDeleter>;
 using scoped_Decoder_CTX = std::unique_ptr<OSSL_DECODER_CTX, 
ssl::detail::DecoderCTXDeleter>;
diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc
index 9c865bbbf8..6e766d6992 100644
--- a/src/iocore/net/SSLUtils.cc
+++ b/src/iocore/net/SSLUtils.cc
@@ -856,15 +856,16 @@ SSLMultiCertConfigLoader::default_server_ssl_ctx()
 static bool
 SSLPrivateKeyHandler(SSL_CTX *ctx, const char *keyPath, const char 
*secret_data, int secret_data_len)
 {
-  EVP_PKEY *pkey = nullptr;
+  // SSL_CTX_use_PrivateKey() takes its own reference on the key, so this
+  // reference must be released on every exit.
+  scoped_PKEY pkey;
 #if HAVE_ENGINE_GET_DEFAULT_RSA && HAVE_ENGINE_LOAD_PRIVATE_KEY
   ENGINE *e = ENGINE_get_default_RSA();
   if (e != nullptr) {
-    pkey = ENGINE_load_private_key(e, keyPath, nullptr, nullptr);
+    pkey.reset(ENGINE_load_private_key(e, keyPath, nullptr, nullptr));
     if (pkey) {
-      if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
+      if (!SSL_CTX_use_PrivateKey(ctx, pkey.get())) {
         Dbg(dbg_ctl_ssl_load, "failed to load server private key from engine");
-        EVP_PKEY_free(pkey);
         return false;
       }
     }
@@ -877,16 +878,16 @@ SSLPrivateKeyHandler(SSL_CTX *ctx, const char *keyPath, 
const char *secret_data,
 
     pem_password_cb *password_cb = SSL_CTX_get_default_passwd_cb(ctx);
     void            *u           = SSL_CTX_get_default_passwd_cb_userdata(ctx);
-    pkey                         = PEM_read_bio_PrivateKey(bio.get(), nullptr, 
password_cb, u);
+
+    pkey.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, password_cb, u));
     if (nullptr == pkey) {
       Dbg(dbg_ctl_ssl_load, "failed to load server private key (%.*s) from 
%s", secret_data_len < 50 ? secret_data_len : 50,
           secret_data, (!keyPath || keyPath[0] == '\0') ? "[empty key path]" : 
keyPath);
       return false;
     }
-    if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
+    if (!SSL_CTX_use_PrivateKey(ctx, pkey.get())) {
       Dbg(dbg_ctl_ssl_load, "failed to attach server private key loaded from 
%s",
           (!keyPath || keyPath[0] == '\0') ? "[empty key path]" : keyPath);
-      EVP_PKEY_free(pkey);
       return false;
     }
     if (e == nullptr && !SSL_CTX_check_private_key(ctx)) {

Reply via email to