如何正确地取消初始化OpenSSL

时间:2015-04-24 10:48:57

标签: c++ windows memory-leaks openssl static-libraries

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

我使用Visual Studio 2013 Express,OpenSSL 1.0.1L(32位和64位),VLD 2.4RC2和我的PC是Windows 7 64位。

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

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:\dd\vctools\crt\crtw32\heap\malloc.c (58): MyLib.dll!_heap_alloc_base
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
    f:\dd\vctools\crt\crtw32\misc\dbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\mem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\lhash\lhash.c (121): MyLib.dll!lh_new + 0x16 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (450): MyLib.dll!int_thread_get + 0x13 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (509): MyLib.dll!int_thread_set_item + 0xF bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (1031): MyLib.dll!ERR_get_state
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (278): MyLib.dll!X509_load_cert_crl_file
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (123): MyLib.dll!by_file_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_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:\dd\vctools\crt\crtw32\heap\malloc.c (58): MyLib.dll!_heap_alloc_base
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
    f:\dd\vctools\crt\crtw32\misc\dbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\mem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\lhash\lhash.c (119): MyLib.dll!lh_new + 0x13 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (450): MyLib.dll!int_thread_get + 0x13 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (509): MyLib.dll!int_thread_set_item + 0xF bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (1031): MyLib.dll!ERR_get_state
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (278): MyLib.dll!X509_load_cert_crl_file
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (123): MyLib.dll!by_file_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_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:\dd\vctools\crt\crtw32\heap\malloc.c (58): MyLib.dll!_heap_alloc_base
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
    f:\dd\vctools\crt\crtw32\misc\dbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\mem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (1019): MyLib.dll!ERR_get_state + 0x17 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (278): MyLib.dll!X509_load_cert_crl_file
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (123): MyLib.dll!by_file_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_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:\dd\vctools\crt\crtw32\heap\malloc.c (58): MyLib.dll!_heap_alloc_base
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (431): MyLib.dll!_heap_alloc_dbg_impl + 0xA bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (239): MyLib.dll!_nh_malloc_dbg_impl + 0x22 bytes
    f:\dd\vctools\crt\crtw32\misc\dbgheap.c (302): MyLib.dll!_nh_malloc_dbg + 0x2A bytes
    f:\dd\vctools\crt\crtw32\misc\dbgmalloc.c (56): MyLib.dll!malloc + 0x21 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\mem.c (312): MyLib.dll!CRYPTO_malloc + 0xF bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\lhash\lhash.c (193): MyLib.dll!lh_insert + 0x15 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (515): MyLib.dll!int_thread_set_item
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (1031): MyLib.dll!ERR_get_state
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\err\err.c (730): MyLib.dll!ERR_put_error + 0x5 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_lib.c (703): MyLib.dll!PEM_read_bio + 0x20 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\pem\pem_info.c (280): MyLib.dll!PEM_X509_INFO_read_bio + 0x10 bytes
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (278): MyLib.dll!X509_load_cert_crl_file
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\by_file.c (123): MyLib.dll!by_file_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_lu.c (120): MyLib.dll!X509_LOOKUP_ctrl
    d:\cfiles\projects\winssl\openssl-1.0.1l\crypto\x509\x509_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.

修改 我将泄漏归结为对SSL_CTX_load_verify_locations的调用。有没有人知道在使用这个功能时我需要取消分配什么?只是评论这个函数会导致泄漏消失,所以不是因为我传递给它的参数。

2 个答案:

答案 0 :(得分:18)

  

d:\ cfiles \项目\ winssl \ OpenSSL的-1.0.1l \加密\ ERR \ err.c

关注此问题,看起来某些错误状态(或字符串)需要免费提供。

  

如何正确取消初始化OpenSSL

启动和关闭的代码如下所示(包括FIPS)。如果您执行加载DH参数之类的操作,那么您也需要清理它们。

博士。 Henson非常乐于帮助他建议为每个线程调用ERR_remove_state。请参阅OpenSSL邮件列表中的Order of Cleanup to avoid memory leaks?

<强> 启动

  • 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 threads(3) man page

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

  

SSL_COMP_get_compression_methods()...

是的,这会导致泄漏。它众所周知,由于ssl_comp_methods被懒惰分配但从未被释放。请参阅OpenSSL Issue 2561: Memory leak with SSL built-in compressions

一些OpenSSL开发人员觉得不值得花时间修复它。请参阅OpenSSL邮件列表中的Small memory leak on multithreaded server

以下是开发者的立场之一:

  

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

     

追踪这一点是浪费时间恕我直言。

这是关于特定泄漏的另一个主题:Preferred way to free ssl_comp_methods?。这就是开发人员的回应:

  

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

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

当他被告知备用用例时,这是他的答复。我想,他建议Oracle和Java重新构建他们的语言。或者不要在其中使用OpenSSL。

  

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

以下是其中一位维护Java VM的人员的回复:

  

作为“替代”JavaVM的维护者,我可以确认我们   绝对必须支持库卸载,因为一个客户是   大量使用它 - 这是在几年前。早期的Sun VM   不支持库卸载,但那些VM也没有   垃圾收集过时的类。

这里有关于修复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 

-----

  

为什么这个帖子被downvoted?我链接的帖子不太详细,它有10个upvotes(加上我的一个)。你们在过去几年里变得更加严格吗?

查看结果原因(which you can't do at the moment),结果投票结果如下:

  

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

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

作为一项政策问题,该网站无法制定规则“除了一些OpenSSL内存泄漏之外总是提供相关代码”(这实际上是我们处理您案例所需要的)。 / p>

答案 1 :(得分:4)

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();