SSL_use_certificate 似乎导致双重释放
SSL_use_certificate seems to be causing a double free
一些上下文
我正在使用 openSSL 在 C++ 中编写一个 transparent/intercepting、支持 HTTPS 的代理。我正在使用 WinDivert 通过我的代理重定向流量。对于我的 SSL 初始化,我的 HTTPSAcceptor 为握手操作的整个服务器上下文生成一个临时 EC_KEY。我在内存中保留了一个 "store"(不是实际的 X509_STORE 对象),我在其中使用 host/SAN DNS 条目作为查找键来欺骗和存储证书。作为旁注,这是我第一次使用 openSSL,所以请更正并原谅我的方法中的任何无知。 :) 也请原谅过度滥用 cout 呕吐 debugging/errors,这些稍后将被包装到记录器中。
肉
无论如何,当我获得传入的 HTTPS 连接时,我要么检索或欺骗,然后检索真正的上游证书。当我生成这些证书时,我使用的是 EC 密钥。代码:
EC_KEY *ecdh = NULL;
if ((ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || EC_KEY_generate_key(ecdh) != 1)
{
std::cout << "In CertStore::GenerateEcKey() - Failed to generate EC_KEY" << std::endl;
}
else
{
EC_KEY_set_asn1_flag(ecdh, OPENSSL_EC_NAMED_CURVE);
EVP_PKEY* pkey = NULL;
pkey = EVP_PKEY_new();
if (pkey == nullptr)
{
std::cout << "In CertStore::GenerateEcKey() - Failed to generate EVP_PKEY" << std::endl;
}
else
{
if (1 != EVP_PKEY_set1_EC_KEY(pkey, ecdh))
{
std::cout << "In CertStore::GenerateEcKey() - Failed EVP_PKEY_set1_EC_KEY" << std::endl;
EVP_PKEY_free(pkey);
return nullptr;
}else{
EC_KEY_up_ref(ecdh);
return pkey;
}
}
}
成功获取欺骗性证书和关联密钥后,显然我会告诉我的 SSL* 对象使用这些进行握手。
if (SSL_use_PrivateKey(m_secureDownstreamSocket->native_handle(), upKey) <= 0)
{
std::cout << "set private key failed" << std::endl;
Kill();
return;
}
if (SSL_use_certificate(m_secureDownstreamSocket->native_handle(), upCert) <= 0)
{
std::cout << "set use cert failed" << std::endl;
Kill();
return;
}
m_secureDownstreamSocket->async_handshake(SslSocket::server, m_strand.wrap(boost::bind(&HttpsBridge::OnDownstreamHandshake, shared_from_this(), boost::asio::placeholders::error)));
但是,这个原因似乎是我的应用程序死于可怕死亡的根源。我曾经在每个 HTTPS 连接(客户端和服务器)上生成一个新的 CTX,但是在阅读了一些文档和一些 SO 帖子之后,我相信正确的方法是使用全局上下文来创建 SSL对象。不管怎样,我要提到的一点是,当我愚蠢地创建大量 CTX 时,我得到的错误很少发生,我将在稍后介绍。由于更改为两个全局 CTX(客户端、服务器),此错误现在发生得非常快,但仍然是随机点。
错误是,不知何故,EC_GROUP 的键被双重释放。问题是我什至不知道为什么他们一开始就被释放了。我在任何 SSL_* 或 SSL_CTX* 方法的文档中都找不到我使用释放任何东西的提及。下面是来自 App Verifier 的事件跟踪,因为 Eclipse 在调试时毫无用处,并且 visual studio 调试器在我拦截和处理本地网络流量时只是以某种方式拒绝工作。请各位网友帮忙。 :(
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<avrf:logfile xmlns:avrf="Application Verifier">
<avrf:logSession TimeStarted="2015-04-05 : 23:51:30" PID="812" Version="2">
<avrf:logEntry Time="2015-04-05 : 23:51:57" LayerName="Heaps" StopCode="0x7" Severity="Error">
<avrf:message>Heap block already freed.</avrf:message>
<avrf:parameter1>8411000 - Heap handle for the heap owning the block.</avrf:parameter1>
<avrf:parameter2>aac49270 - Heap block being freed again.</avrf:parameter2>
<avrf:parameter3>20 - Size of the heap block.</avrf:parameter3>
<avrf:parameter4>0 - Not used</avrf:parameter4>
<avrf:stackTrace>
<avrf:trace>vrfcore!VerifierDisableVerifier+948 ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierStopMessage+a0 ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+318b ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+8a6 ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+94b ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierCheckPageHeapAllocation+40 ( @ 0)</avrf:trace>
<avrf:trace>vfbasics!+7ff99e7f3773 ( @ 0)</avrf:trace>
<avrf:trace>msvcrt!setjmp+123 ( @ 0)</avrf:trace>
<avrf:trace>vfbasics!+7ff99e7f4606 ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!CRYPTO_free+2b ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!BN_free+29 ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_GROUP_cmp+307 ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_GROUP_free+2c ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_KEY_set_group+2b ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_GF2m_simple_method+180f ( @ 0)</avrf:trace>
<avrf:trace>SSLEAY32!SSL_use_PrivateKey_ASN1+1a5 ( @ 0)</avrf:trace>
<avrf:trace>SSLEAY32!SSL_use_certificate+9a ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+407ef6 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a2dbf ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a2e0a ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45b6b1 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45c50e ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+488870 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+461241 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451908 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47d3a0 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451938 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+472739 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45e9c4 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+474001 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a4098 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+465a7d ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+488af1 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47774c ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+461001 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451488 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47ce40 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4514b8 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+478de7 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+470f8b ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45e2c7 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47d3f4 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451e18 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+464f44 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451e48 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4819b1 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47cc68 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47f2d2 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47ecb8 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45db6c ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a2c75 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45b32c ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45ce36 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+48ce4e ( @ 0)</avrf:trace>
<avrf:trace>libboost_thread!ZN5boost6detail23get_current_thread_dataEv+729 ( @ 0)</avrf:trace>
<avrf:trace>msvcrt!strupr+c3 ( @ 0)</avrf:trace>
<avrf:trace>msvcrt!endthreadex+9d ( @ 0)</avrf:trace>
<avrf:trace>vfbasics!+7ff99e7fc729 ( @ 0)</avrf:trace>
<avrf:trace>KERNEL32!BaseThreadInitThunk+22 ( @ 0)</avrf:trace>
<avrf:trace>ntdll!RtlUserThreadStart+34 ( @ 0)</avrf:trace>
</avrf:stackTrace>
</avrf:logEntry>
</avrf:logSession>
</avrf:logfile>
P.S。 - 让我感到奇怪的一件事是堆栈跟踪显示对 SSL_use_PrivateKey_ASN1 的调用。不知道为什么,因为我只是每次调用 SSL_use_cert 和 SSL_use_prvkey。 会不会是 use_cert 试图从证书中提取私钥?只是有这个想法,我要调查 更新 - 不。出于良好的安全原因,不可能将私钥添加到 X509 结构。是一个绝望的、未经深思熟虑的想法。
所以,Michael Foukarakis 帮助我质疑我在评论中的一些假设,最终导致我解决了这个问题,所以完全归功于他。这是我的方法出错的地方以及解决方法。
如问题中所述,我最初是为每个代理连接创建一个新的 SSL_CTX
(boost::asio::ssl::context
) 对象:一个用于下游,一个用于上游。上游,作为客户端的代理,在初始化期间在其上下文中有 boost::asio::ssl::context::load_verify_file
("ca-bundle.crt"),这导致应用程序的 ram 消耗大量膨胀。
原始问题的第二部分是我正在为每个下游连接创建一个新的 SSL_CTX
,连接到我们的客户端的连接,我们在其中提供欺骗性证书并伪装成原始服务器进行握手.每个新连接 "bridge" 都会在构造时传递对 "CertStore" 对象的引用,该对象旨在欺骗、存储和检索证书及其密钥对,由主机名索引。
所以我们要做的是在一个中心位置保存证书和密钥,但是每个单独的 SSL_CTX
对象都被分配了这些证书和密钥,然后在连接关闭时销毁,最终,"randomly" 会导致某处出现双重释放,因为 openSSL 在(大多数?)对象上使用内部引用计数。这些引用将随着 SSL_CTX
的创建而增加和减少,在它们上调用 SSL_CTX_use_certificate
和 SSL_CTX_use_PrivateKey
,然后被销毁。最终,两个 SSL_CTX
的死亡将持有对引用计数为 1 的同一证书或密钥对的引用,导致当两个上下文最终死亡时内存双倍释放。
解决方案是使用一个共享上下文,您可以在某个地方保持安全和活动,直到上游(客户端)连接的应用程序关闭。在该单个上下文上调用 boost::asio::ssl::context::load_verify_file
("ca-bundle.crt") 一次,然后从中生成所有客户端 SSL 对象。对于服务器上下文,为您所充当的每个主机创建一个服务器上下文,设置该上下文的证书和私钥,然后在代表该主机的所有下游 SSL* 套接字之间共享该上下文。
我被这个问题困扰了,谢谢你的跟进回答。
我的解决方案不是在创建它的析构函数中删除 ssl::context 实例,而是将删除发布到主 io_service:
类似于:
MyThing::~MyThing() {
boost::asio::ssl::context *c = ssl_context_;
socket_.get_io_service().post([c](){ delete c; });
}
这对我来说似乎很好地治愈了它。 (我使用 new/delete 因为我只根据需要创建它)。
我认为一定有更确定的方法来做到这一点,也许 shared_ptr 但我还没有弄清楚。
一些上下文
我正在使用 openSSL 在 C++ 中编写一个 transparent/intercepting、支持 HTTPS 的代理。我正在使用 WinDivert 通过我的代理重定向流量。对于我的 SSL 初始化,我的 HTTPSAcceptor 为握手操作的整个服务器上下文生成一个临时 EC_KEY。我在内存中保留了一个 "store"(不是实际的 X509_STORE 对象),我在其中使用 host/SAN DNS 条目作为查找键来欺骗和存储证书。作为旁注,这是我第一次使用 openSSL,所以请更正并原谅我的方法中的任何无知。 :) 也请原谅过度滥用 cout 呕吐 debugging/errors,这些稍后将被包装到记录器中。
肉
无论如何,当我获得传入的 HTTPS 连接时,我要么检索或欺骗,然后检索真正的上游证书。当我生成这些证书时,我使用的是 EC 密钥。代码:
EC_KEY *ecdh = NULL;
if ((ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || EC_KEY_generate_key(ecdh) != 1)
{
std::cout << "In CertStore::GenerateEcKey() - Failed to generate EC_KEY" << std::endl;
}
else
{
EC_KEY_set_asn1_flag(ecdh, OPENSSL_EC_NAMED_CURVE);
EVP_PKEY* pkey = NULL;
pkey = EVP_PKEY_new();
if (pkey == nullptr)
{
std::cout << "In CertStore::GenerateEcKey() - Failed to generate EVP_PKEY" << std::endl;
}
else
{
if (1 != EVP_PKEY_set1_EC_KEY(pkey, ecdh))
{
std::cout << "In CertStore::GenerateEcKey() - Failed EVP_PKEY_set1_EC_KEY" << std::endl;
EVP_PKEY_free(pkey);
return nullptr;
}else{
EC_KEY_up_ref(ecdh);
return pkey;
}
}
}
成功获取欺骗性证书和关联密钥后,显然我会告诉我的 SSL* 对象使用这些进行握手。
if (SSL_use_PrivateKey(m_secureDownstreamSocket->native_handle(), upKey) <= 0)
{
std::cout << "set private key failed" << std::endl;
Kill();
return;
}
if (SSL_use_certificate(m_secureDownstreamSocket->native_handle(), upCert) <= 0)
{
std::cout << "set use cert failed" << std::endl;
Kill();
return;
}
m_secureDownstreamSocket->async_handshake(SslSocket::server, m_strand.wrap(boost::bind(&HttpsBridge::OnDownstreamHandshake, shared_from_this(), boost::asio::placeholders::error)));
但是,这个原因似乎是我的应用程序死于可怕死亡的根源。我曾经在每个 HTTPS 连接(客户端和服务器)上生成一个新的 CTX,但是在阅读了一些文档和一些 SO 帖子之后,我相信正确的方法是使用全局上下文来创建 SSL对象。不管怎样,我要提到的一点是,当我愚蠢地创建大量 CTX 时,我得到的错误很少发生,我将在稍后介绍。由于更改为两个全局 CTX(客户端、服务器),此错误现在发生得非常快,但仍然是随机点。
错误是,不知何故,EC_GROUP 的键被双重释放。问题是我什至不知道为什么他们一开始就被释放了。我在任何 SSL_* 或 SSL_CTX* 方法的文档中都找不到我使用释放任何东西的提及。下面是来自 App Verifier 的事件跟踪,因为 Eclipse 在调试时毫无用处,并且 visual studio 调试器在我拦截和处理本地网络流量时只是以某种方式拒绝工作。请各位网友帮忙。 :(
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<avrf:logfile xmlns:avrf="Application Verifier">
<avrf:logSession TimeStarted="2015-04-05 : 23:51:30" PID="812" Version="2">
<avrf:logEntry Time="2015-04-05 : 23:51:57" LayerName="Heaps" StopCode="0x7" Severity="Error">
<avrf:message>Heap block already freed.</avrf:message>
<avrf:parameter1>8411000 - Heap handle for the heap owning the block.</avrf:parameter1>
<avrf:parameter2>aac49270 - Heap block being freed again.</avrf:parameter2>
<avrf:parameter3>20 - Size of the heap block.</avrf:parameter3>
<avrf:parameter4>0 - Not used</avrf:parameter4>
<avrf:stackTrace>
<avrf:trace>vrfcore!VerifierDisableVerifier+948 ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierStopMessage+a0 ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+318b ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+8a6 ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierDisableFaultInjectionExclusionRange+94b ( @ 0)</avrf:trace>
<avrf:trace>verifier!VerifierCheckPageHeapAllocation+40 ( @ 0)</avrf:trace>
<avrf:trace>vfbasics!+7ff99e7f3773 ( @ 0)</avrf:trace>
<avrf:trace>msvcrt!setjmp+123 ( @ 0)</avrf:trace>
<avrf:trace>vfbasics!+7ff99e7f4606 ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!CRYPTO_free+2b ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!BN_free+29 ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_GROUP_cmp+307 ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_GROUP_free+2c ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_KEY_set_group+2b ( @ 0)</avrf:trace>
<avrf:trace>LIBEAY32!EC_GF2m_simple_method+180f ( @ 0)</avrf:trace>
<avrf:trace>SSLEAY32!SSL_use_PrivateKey_ASN1+1a5 ( @ 0)</avrf:trace>
<avrf:trace>SSLEAY32!SSL_use_certificate+9a ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+407ef6 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a2dbf ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a2e0a ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45b6b1 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45c50e ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+488870 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+461241 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451908 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47d3a0 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451938 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+472739 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45e9c4 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+474001 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a4098 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+465a7d ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+488af1 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47774c ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+461001 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451488 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47ce40 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4514b8 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+478de7 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+470f8b ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45e2c7 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47d3f4 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451e18 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+464f44 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+451e48 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4819b1 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47cc68 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47f2d2 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+47ecb8 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45db6c ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+4a2c75 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45b32c ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+45ce36 ( @ 0)</avrf:trace>
<avrf:trace>SSITM!+48ce4e ( @ 0)</avrf:trace>
<avrf:trace>libboost_thread!ZN5boost6detail23get_current_thread_dataEv+729 ( @ 0)</avrf:trace>
<avrf:trace>msvcrt!strupr+c3 ( @ 0)</avrf:trace>
<avrf:trace>msvcrt!endthreadex+9d ( @ 0)</avrf:trace>
<avrf:trace>vfbasics!+7ff99e7fc729 ( @ 0)</avrf:trace>
<avrf:trace>KERNEL32!BaseThreadInitThunk+22 ( @ 0)</avrf:trace>
<avrf:trace>ntdll!RtlUserThreadStart+34 ( @ 0)</avrf:trace>
</avrf:stackTrace>
</avrf:logEntry>
</avrf:logSession>
</avrf:logfile>
P.S。 - 让我感到奇怪的一件事是堆栈跟踪显示对 SSL_use_PrivateKey_ASN1 的调用。不知道为什么,因为我只是每次调用 SSL_use_cert 和 SSL_use_prvkey。 会不会是 use_cert 试图从证书中提取私钥?只是有这个想法,我要调查 更新 - 不。出于良好的安全原因,不可能将私钥添加到 X509 结构。是一个绝望的、未经深思熟虑的想法。
所以,Michael Foukarakis 帮助我质疑我在评论中的一些假设,最终导致我解决了这个问题,所以完全归功于他。这是我的方法出错的地方以及解决方法。
如问题中所述,我最初是为每个代理连接创建一个新的 SSL_CTX
(boost::asio::ssl::context
) 对象:一个用于下游,一个用于上游。上游,作为客户端的代理,在初始化期间在其上下文中有 boost::asio::ssl::context::load_verify_file
("ca-bundle.crt"),这导致应用程序的 ram 消耗大量膨胀。
原始问题的第二部分是我正在为每个下游连接创建一个新的 SSL_CTX
,连接到我们的客户端的连接,我们在其中提供欺骗性证书并伪装成原始服务器进行握手.每个新连接 "bridge" 都会在构造时传递对 "CertStore" 对象的引用,该对象旨在欺骗、存储和检索证书及其密钥对,由主机名索引。
所以我们要做的是在一个中心位置保存证书和密钥,但是每个单独的 SSL_CTX
对象都被分配了这些证书和密钥,然后在连接关闭时销毁,最终,"randomly" 会导致某处出现双重释放,因为 openSSL 在(大多数?)对象上使用内部引用计数。这些引用将随着 SSL_CTX
的创建而增加和减少,在它们上调用 SSL_CTX_use_certificate
和 SSL_CTX_use_PrivateKey
,然后被销毁。最终,两个 SSL_CTX
的死亡将持有对引用计数为 1 的同一证书或密钥对的引用,导致当两个上下文最终死亡时内存双倍释放。
解决方案是使用一个共享上下文,您可以在某个地方保持安全和活动,直到上游(客户端)连接的应用程序关闭。在该单个上下文上调用 boost::asio::ssl::context::load_verify_file
("ca-bundle.crt") 一次,然后从中生成所有客户端 SSL 对象。对于服务器上下文,为您所充当的每个主机创建一个服务器上下文,设置该上下文的证书和私钥,然后在代表该主机的所有下游 SSL* 套接字之间共享该上下文。
我被这个问题困扰了,谢谢你的跟进回答。
我的解决方案不是在创建它的析构函数中删除 ssl::context 实例,而是将删除发布到主 io_service:
类似于:
MyThing::~MyThing() {
boost::asio::ssl::context *c = ssl_context_;
socket_.get_io_service().post([c](){ delete c; });
}
这对我来说似乎很好地治愈了它。 (我使用 new/delete 因为我只根据需要创建它)。
我认为一定有更确定的方法来做到这一点,也许 shared_ptr 但我还没有弄清楚。