fix(asio): memory leaks over ssl::stream lifetime

asio::ssl::stream leaks a significant amount (>20KB) of memory over its
lifetime.

asio::ssl::mbedtls::engine::impl has these members ...

	mbedtls_ssl_context ssl_{};
	mbedtls_entropy_context entropy_{};
	mbedtls_ctr_drbg_context ctr_drbg_{};
	mbedtls_ssl_config conf_{};
	mbedtls_x509_crt public_cert_{};
	mbedtls_pk_context pk_key_{};
	mbedtls_x509_crt ca_cert_{};

... which are properly init'ed in its constructor ...

	mbedtls_ssl_init(&ssl_);
	mbedtls_ssl_config_init(&conf_);
	mbedtls_ctr_drbg_init(&ctr_drbg_);
	mbedtls_entropy_init(&entropy_);
	mbedtls_x509_crt_init(&public_cert_);
	mbedtls_pk_init(&pk_key_);
	mbedtls_x509_crt_init(&ca_cert_);

... but are never free'd ... until now

	~impl()
	{
	    mbedtls_x509_crt_free(&ca_cert_);
	    mbedtls_pk_free(&pk_key_);
	    mbedtls_x509_crt_free(&public_cert_);
	    mbedtls_entropy_free(&entropy_);
	    mbedtls_ctr_drbg_free(&ctr_drbg_);
	    mbedtls_ssl_config_free(&conf_);
	    mbedtls_ssl_free(&ssl_);
	}

asio::ssl::mbedtls::engine::impl::configure calls ...

	mbedtls_x509_crt_init(&public_cert_);
	mbedtls_pk_init(&pk_key_);
	mbedtls_x509_crt_init(&ca_cert_);

... again without first free'ing any resources that might be held by such.
now this is done first:

	mbedtls_x509_crt_free(&ca_cert_);
	mbedtls_pk_free(&pk_key_);
	mbedtls_x509_crt_free(&public_cert_);

asio::ssl::engine has this member

	std::pair<std::shared_ptr<bio>, std::shared_ptr<bio>> bio_;

which is made in its constructor

	explicit engine(std::shared_ptr<context> ctx): ctx_(std::move(ctx)),
	    bio_(bio::new_pair("mbedtls-engine")), state_(IDLE), verify_mode_(0) {}

asio::ssl::mbedtls::bio::new_pair creates a cyclic reference between its
paired elements

	static std::pair<std::shared_ptr<bio>, std::shared_ptr<bio>>  new_pair(const char *error_location)
	{
	    auto b1 = std::shared_ptr<bio>(new (std::nothrow) bio);
	    auto b2 = std::shared_ptr<bio>(new (std::nothrow) bio);
	    if (b1 == nullptr || b2 == nullptr) {
	        throw_alloc_failure(error_location);
	    } else {
	        b1->peer_ = b2;
	        b2->peer_ = b1;
	    }
	    return std::make_pair(b1, b2);
	}

there is no asio::ssl::engine destructor to untie this cycle so when the
pair member is destroyed, its elements will leak.
a destructor is needed to fix this ...

	~engine()
	{
	    bio::untie_pair(bio_);
	}

... along with untie_pair ...

	// untie cyclic shared_ptr references made by new_pair in preparation for destruction
	static void untie_pair(std::pair<std::shared_ptr<bio>, std::shared_ptr<bio>>& pair)
	{
	    if (pair.first) {
	        pair.first->peer_.reset();
	    }
	    if (pair.second) {
	        pair.second->peer_.reset();
	    }
	}
This commit is contained in:
Ross Tyler
2025-12-19 07:57:52 -08:00
parent 767a090dc5
commit d33cb616da
2 changed files with 30 additions and 0 deletions
@@ -100,6 +100,17 @@ public:
return std::make_pair(b1, b2);
}
// untie cyclic shared_ptr references made by new_pair in preparation for destruction
static void untie_pair(std::pair<std::shared_ptr<bio>, std::shared_ptr<bio>>& pair)
{
if (pair.first) {
pair.first->peer_.reset();
}
if (pair.second) {
pair.second->peer_.reset();
}
}
private:
std::array<uint8_t, BIO_SIZE> data_ {};
size_t size_ {BIO_SIZE};
@@ -62,6 +62,11 @@ public:
explicit engine(std::shared_ptr<context> ctx): ctx_(std::move(ctx)),
bio_(bio::new_pair("mbedtls-engine")), state_(IDLE), verify_mode_(0) {}
~engine()
{
bio::untie_pair(bio_);
}
void set_verify_mode(asio::ssl::verify_mode mode)
{
verify_mode_ = mode;
@@ -232,8 +237,22 @@ private:
mbedtls_x509_crt_init(&ca_cert_);
}
~impl()
{
mbedtls_x509_crt_free(&ca_cert_);
mbedtls_pk_free(&pk_key_);
mbedtls_x509_crt_free(&public_cert_);
mbedtls_entropy_free(&entropy_);
mbedtls_ctr_drbg_free(&ctr_drbg_);
mbedtls_ssl_config_free(&conf_);
mbedtls_ssl_free(&ssl_);
}
bool configure(context *ctx, bool is_client_not_server, int mbedtls_verify_mode)
{
mbedtls_x509_crt_free(&ca_cert_);
mbedtls_pk_free(&pk_key_);
mbedtls_x509_crt_free(&public_cert_);
mbedtls_x509_crt_init(&public_cert_);
mbedtls_pk_init(&pk_key_);
mbedtls_x509_crt_init(&ca_cert_);