如何正确取消初始化OpenSSL

How to properly uninitialize OpenSSL

本文关键字:OpenSSL 初始化 取消 何正确      更新时间:2023-10-16

在我的OpenSSL客户端中,我遇到了一个问题,即当我选择静态链接libeay32和ssleay32而不是动态链接时,我从Visual leak Detector中收到了大量内存泄漏错误。我在这个线程中复制了OP中的命令,但我还有6个。然后,我在同一线程中添加了4LegsDrivenCat建议的sk_SSL_COMP_free(SSL_COMP_get_compression_methods());,只剩下4个,所有这些显然都与加载可信证书有关,我用它来与服务器的证书进行比较。

我使用Visual Studio 2013学习版、OpenSSL 1.0.1L(32位和64位)、VLD 2.4RC2,我的电脑是Windows 7 64位。

下面的调用堆栈来自安全模式下的VLD的64位。在32位VLD在安全模式下崩溃(而它在快速模式下工作,但没有产生一个像样的调用堆栈)。我删除了调用堆栈中引用我自己的函数以及十六进制数据的部分。

Visual Leak Detector Version 2.4RC2 installed.
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 5671 at 0x000000000097E9B0: 180 bytes ----------
Leak Hash: 0xA14DA3AA, Count: 1, Total 180 bytes
Call Stack (TID 7088):
0x000000007746FAC0 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:ddvctoolscrtcrtw32heapmalloc.c (58): MyLib.dll!_heap_alloc_base
f:ddvctoolscrtcrtw32miscdbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
f:ddvctoolscrtcrtw32miscdbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptomem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptolhashlhash.c (121): MyLib.dll!lh_new + 0x16 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (450): MyLib.dll!int_thread_get + 0x13 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (509): MyLib.dll!int_thread_set_item + 0xF bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (1031): MyLib.dll!ERR_get_state
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (278): MyLib.dll!X509_load_cert_crl_file
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (123): MyLib.dll!by_file_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_d2.c (92): MyLib.dll!X509_STORE_load_locations + 0x1D bytes
---------- Block 5670 at 0x000000001AC815C0: 164 bytes ----------
Leak Hash: 0x38C8916E, Count: 1, Total 164 bytes
Call Stack (TID 7088):
0x000000007746FAC0 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:ddvctoolscrtcrtw32heapmalloc.c (58): MyLib.dll!_heap_alloc_base
f:ddvctoolscrtcrtw32miscdbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
f:ddvctoolscrtcrtw32miscdbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptomem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptolhashlhash.c (119): MyLib.dll!lh_new + 0x13 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (450): MyLib.dll!int_thread_get + 0x13 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (509): MyLib.dll!int_thread_set_item + 0xF bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (1031): MyLib.dll!ERR_get_state
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (278): MyLib.dll!X509_load_cert_crl_file
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (123): MyLib.dll!by_file_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_d2.c (92): MyLib.dll!X509_STORE_load_locations + 0x1D bytes
---------- Block 5669 at 0x000000001ADABE80: 588 bytes ----------
Leak Hash: 0xC3E47B0F, Count: 1, Total 588 bytes
Call Stack (TID 7088):
0x000000007746FAC0 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:ddvctoolscrtcrtw32heapmalloc.c (58): MyLib.dll!_heap_alloc_base
f:ddvctoolscrtcrtw32miscdbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
f:ddvctoolscrtcrtw32miscdbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptomem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (1019): MyLib.dll!ERR_get_state + 0x17 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (278): MyLib.dll!X509_load_cert_crl_file
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (123): MyLib.dll!by_file_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_d2.c (92): MyLib.dll!X509_STORE_load_locations + 0x1D bytes
---------- Block 5672 at 0x000000001ADC4180: 76 bytes ----------
Leak Hash: 0x02B2EA5E, Count: 1, Total 76 bytes
Call Stack (TID 7088):
0x000000007746FAC0 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:ddvctoolscrtcrtw32heapmalloc.c (58): MyLib.dll!_heap_alloc_base
f:ddvctoolscrtcrtw32miscdbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
f:ddvctoolscrtcrtw32miscdbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
f:ddvctoolscrtcrtw32miscdbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptomem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptolhashlhash.c (193): MyLib.dll!lh_insert + 0x15 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (515): MyLib.dll!int_thread_set_item
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (1031): MyLib.dll!ERR_get_state
d:cfilesprojectswinsslopenssl-1.0.1lcryptoerrerr.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptopempem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (278): MyLib.dll!X509_load_cert_crl_file
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509by_file.c (123): MyLib.dll!by_file_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
d:cfilesprojectswinsslopenssl-1.0.1lcryptox509x509_d2.c (92): MyLib.dll!X509_STORE_load_locations + 0x1D bytes

Visual Leak Detector detected 4 memory leaks (1008 bytes).
Largest number used: 529114 bytes.
Total allocations: 1070421 bytes.
Visual Leak Detector is now exiting.

edit:我将泄漏归因于对SSL_CTX_load_verify_locations的调用。有人知道我在使用这个函数时需要取消分配什么吗?只是注释这个函数会导致泄漏消失,所以这不是因为我传递给它的参数

d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c

锁定此项,看起来有些错误状态(或字符串)需要释放。


如何正确取消初始化OpenSSL

启动和关闭的代码如下所示(包括FIPS)。如果你做一些事情,比如加载DH参数,那么你也需要清理它们。

Dr。Henson建议为每个线程调用ERR_remove_state,这对他很有帮助。请参阅"清理顺序"以避免内存泄漏?在OpenSSL邮件列表中。

启动

  • SSL_library_init();
  • SSL_load_error_strings();
  • FIPS_mode_set(1);
  • CRYPTO_set_id_callback(<fn>);
  • CRYPTO_set_locking_callback(<fn>);

关闭

  • FIPS_mode_set(0);
  • CRYPTO_set_locking_callback(NULL);
  • CRYPTO_set_id_callback(NULL);
  • ENGINE_cleanup();
  • CONF_modules_unload();
  • ERR_free_strings();
  • EVP_cleanup();
  • CRYPTO_cleanup_all_ex_data();

并且,对于每个线程:

  • ERR_remove_state();

如果程序是多线程的,则只需要CRYPTO_set_id_callbackCRYPTO_set_locking_callback。请参阅Openssl线程(3)手册页。

我相信您可以在OpenSSL 1.0.2及更高版本中调用SSL_COMP_free_compression_methods。这解决了下面的一些投诉。但它在OpenSSL 1.0.1及以下版本中不可用。


SSL_COMP_get_compression_methods()。。。

是的,这会导致泄漏。它是众所周知的,并且由于ssl_comp_methods被延迟分配但从未被释放。请参阅OpenSSL Issue 2561:SSL内置压缩的内存泄漏。

一些OpenSSL开发人员认为不值得花时间来修复它。请参阅OpenSSL邮件列表中的多线程服务器上的小内存泄漏。

以下是开发人员对它的立场之一:

未释放的固定内存量,与执行的操作数不是内存泄漏。图书馆在一段时间内为进程的生存期分配内存初始化或首次使用函数。这很正常。

IMHO追踪这件事是浪费时间。

这里有另一个关于特定泄漏的线程:释放ssl_comp_methods的首选方法?。这是同一个开发者的回应:

为什么有人痴迷于释放分配给静态的内存指针最多一次。不存在与此相关的"内存泄漏"分配,因为使用的额外内存量是固定的。

他没有意识到的是,Java和.Net会在程序的生命周期中多次加载/卸载库,因此少量的"谁在乎"可以无限增长。

当他被告知备用用例时,以下是他的回答。我猜,他是在建议Oracle和Java重新设计他们的语言。或者不要在其中使用OpenSSL。

卸载共享库通常是不安全的。

这是一位维护Java VM的人员的回应:

作为"替代"JavaVM的维护者,我可以确认因为一位客户大量使用它——那是几年前的事了。早期Sun虚拟机不支持库卸载,但这些虚拟机也不支持垃圾收集过时的类。


这里是关于修复ssl_comp_methods泄漏的部分。

在任何情况下,您都需要添加下面描述的修补程序。

要让程序手动执行,只需添加一个名为free_compressions(或类似函数)的函数,并像上面列出的所有其他方法一样在关闭时调用它。该函数需要导出。

要在Linux下自动完成,需要一些技巧。您必须使用GCC扩展名:__attribute__ ((destructor))

/* Add to end of <openssl dir>/ssl/ssl_ciph.c */
#if !defined(OPENSSL_NO_COMP) && defined(__GNU_C__)
void free_compressions(void) __attribute__ ((destructor));
void free_compressions(void)
{
if (ssl_comp_methods != NULL)
{
sk_SSL_COMP_free(ssl_comp_methods);
ssl_comp_methods = NULL;
}
}
#endif 

要在Windows下自动执行,必须在DllMain中执行。但你必须小心你在DllMain中做了什么,以及你是如何做的

/* Add to end of <openssl dir>/ssl/ssl.h*/
#if !defined(OPENSSL_NO_COMP) && defined(WIN32)
__declspec(dllexport)
void free_compressions(void);
#endof
/* Add to end of <openssl dir>/ssl/ssl_ciph.c */
#if !defined(OPENSSL_NO_COMP) && defined(WIN32)
void free_compressions(void)
{
if (ssl_comp_methods != NULL)
{
sk_SSL_COMP_free(ssl_comp_methods);
ssl_comp_methods = NULL;
}
}
#endif 

-----

为什么这个线程被否决?我链接的帖子没有那么详细,它获得了10张赞成票(加上我的一张)。你们是不是在过去几年里变得更加严格了?

看看势均力敌的原因(你现在不能这么做),投下了势均力战的票,原因是:

寻求调试帮助的问题("为什么这个代码不起作用?")必须包括所需的行为、特定的问题或错误,以及在问题本身中重现它所需的最短代码

通常适用。但在你的情况下,它不是;对于那些不熟悉这个问题的人来说,这并不明显。事实上,你可以写一个简单的程序,只需初始化并统一库,它可能会泄漏。。。

根据政策,该网站不能制定规则"始终提供相关代码,除非有一些OpenSSL内存泄漏">(这实际上是我们处理您的案件所需要的)。

jww帖子中添加了一个小部分,如果您的OpenSSL版本是用zlib库构建的,那么您应该添加COMP_zlib_cleanup();至停机部分。因为它的DSO模块在默认情况下不会被释放。因此,完整的关闭代码应该是:

FIPS_mode_set(0);
CRYPTO_set_locking_callback(nullptr);
CRYPTO_set_id_callback(nullptr);
ERR_remove_state(0);
SSL_COMP_free_compression_methods();
ENGINE_cleanup();
CONF_modules_free();
CONF_modules_unload(1);
COMP_zlib_cleanup();
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();