c++ windows memory-leaks openssl static-libraries

c++ - Cómo desinicializar correctamente OpenSSL



windows memory-leaks (2)

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

Al aferrarse a esto, parece que se debe liberar algún estado de error (o cadenas).

Cómo desinicializar correctamente OpenSSL

El código de inicio y apagado se muestra a continuación (incluido FIPS). Si hace cosas como cargar parámetros DH, también deberá limpiarlos.

El Dr. Henson fue muy útil con su sugerencia de llamar a ERR_remove_state para cada hilo. ¿Ver Orden de limpieza para evitar pérdidas de memoria? en la lista de correo de OpenSSL.

Puesta en marcha

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

Apagar

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

Y, para cada hilo:

  • ERR_remove_state();

Solo necesita CRYPTO_set_id_callback y CRYPTO_set_locking_callback si su programa es multiproceso. Consulte la página del comando man hilos de Openssl (3) .

Creo que puede llamar a SSL_COMP_free_compression_methods en OpenSSL 1.0.2 y superior. Eso aborda algunas de las quejas a continuación. Pero no está disponible en OpenSSL 1.0.1 y versiones posteriores.

SSL_COMP_get_compression_methods () ...

Sí, esto causa una fuga. Es bien conocido y debido a que ssl_comp_methods se asignó perezosamente pero nunca se liberó. Consulte el número 2561 de OpenSSL: pérdida de memoria con compresiones incorporadas SSL .

Algunos de los desarrolladores de OpenSSL no sienten que valga la pena repararlo. Consulte Pérdida de memoria pequeña en un servidor multiproceso en la lista de correo de OpenSSL.

La siguiente es una de las posiciones del desarrollador al respecto:

Una cantidad fija de memoria que no se desasigna y es independiente del número de operaciones realizadas, NO es una pérdida de memoria. Bibliotecas para asignar memoria durante la vida útil del proceso durante la inicialización única o el primer uso de una función. Esto es normal.

Rastrear esto es una pérdida de tiempo en mi humilde opinión.

Y aquí hay otro hilo sobre esa fuga en particular: ¿ forma preferida de liberar ssl_comp_methods? . Y aquí está la respuesta de ese mismo desarrollador:

¿Por qué alguien está obsesionado con liberar memoria asignada a punteros estáticos como máximo una vez? No hay "pérdida de memoria" asociada con tales asignaciones porque la cantidad de memoria adicional utilizada es fija.

De lo que no se dio cuenta es de que Java y .Net cargarán / descargarán la biblioteca muchas veces durante el ciclo de vida de un programa, por lo que una pequeña cantidad de "a quién le importa" puede crecer sin límites.

Cuando le dijeron sobre el caso de uso alternativo, aquí estaba su respuesta. Supongo que sugiere que Oracle y Java rediseñen sus lenguajes. O no use OpenSSL en ellos.

La descarga de bibliotecas compartidas generalmente no es segura.

Aquí fue la respuesta de una de las personas que mantienen una máquina virtual Java:

Como mantenedor de un JavaVM "alternativo" puedo confirmar que tuvimos que admitir absolutamente la descarga de la biblioteca porque un cliente lo estaba usando mucho, y eso fue hace unos años. Las primeras máquinas virtuales de Sun no admitían la descarga de bibliotecas, pero esas máquinas virtuales tampoco admitían clases obsoletas de recolección de basura.

Aquí una sección sobre ssl_comp_methods arreglar la fuga ssl_comp_methods .

En todos los casos, deberá agregar el parche que se describe a continuación.

Para que un programa lo haga manualmente, simplemente agregue una función llamada free_compressions (o similar) y llame al apagado como todos los otros métodos enumerados anteriormente. La función necesita ser exportada.

Para hacerlo automáticamente en Linux, se necesita un pequeño truco. __attribute__ ((destructor)) usar una extensión 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

Para hacerlo automáticamente en Windows, debe hacerlo en DllMain . Pero debe tener cuidado con lo que hace en DllMain y cómo lo hace. Entonces tal vez algo como:

/* 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

-----

¿Por qué se está rechazando este hilo? El hilo que vinculé es mucho menos detallado y obtuvo 10 votos a favor (más uno mío). ¿Se volvieron mucho más estrictos en los últimos años?

Mirando el motivo cerrado ( que no puede hacer en este momento ), se emitió el voto cerrado con el motivo:

Las preguntas que buscan ayuda para la depuración ("¿por qué no funciona este código?") Deben incluir el comportamiento deseado, un problema o error específico y el código más corto necesario para reproducirlo en la pregunta misma.

Normalmente eso aplica. Pero en tu caso no lo hace; y no es evidente para aquellos que no están familiarizados con el tema. De hecho, podría escribir un programa simple que simplemente inicialice y luego unifique la biblioteca y probablemente tenga fugas ...

Como cuestión de política, el sitio no puede establecer una regla "Siempre proporcione el código relevante, excepto por algunas pérdidas de memoria de OpenSSL" (que es efectivamente lo que necesitamos para manejar su caso).

En mi cliente OpenSSL tengo el problema de que en el momento en que elegí vincular libeay32 y ssleay32 estáticamente en lugar de dinámicamente, obtuve toneladas de errores de pérdida de memoria del Detector de fugas visuales. Copié los comandos del OP en este hilo , pero todavía me quedaban 6. Luego agregué sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); como lo aconsejó 4LegsDrivenCat en el mismo hilo y solo quedaron 4 más, todo lo cual aparentemente está relacionado con la carga de un certificado de confianza que uso para compararlo con el certificado del servidor.

Uso Visual Studio 2013 Express, OpenSSL 1.0.1L (tanto de 32 como de 64 bits), VLD 2.4RC2 y mi PC es Windows 7 de 64 bits.

La pila de llamadas a continuación es de 64 bits de VLD en modo seguro. En 32 bits, el VLD se bloqueó en modo seguro (mientras funciona en modo rápido pero no produce una pila de llamadas decente). Eliminé las partes de la pila de llamadas que se referían a mis propias funciones, así como a los datos hexadecimales.

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.

editar: fijé las filtraciones a la llamada a SSL_CTX_load_verify_locations. ¿Alguien sabe lo que necesito desasignar al usar esta función? Solo comentar esta función hace que las fugas desaparezcan, por lo que no se debe a los parámetros que le paso.


Pequeña adición a la publicación jww , si su versión OpenSSL está construida con la biblioteca zlib, entonces debe agregar COMP_zlib_cleanup (); para cerrar la sección. Porque su módulo DSO no está liberado por defecto. Entonces, el código de apagado completo debería ser:

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