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)) {