Cómo desinicializar correctamente OpenSSL

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 de Visual Leak Detector. 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()); según lo informado por 4LegsDrivenCat en el mismo hilo y solo quedaron 4 más, todos los cuales aparentemente están relacionados 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 (32 y 64 Bit), VLD 2.4RC2 y mi PC es Windows 7 64 Bit.

La siguiente lista de llamadas es 64 Bit de VLD en modo seguro. En 32 Bit VLD se colgó en modo seguro (mientras funciona en modo rápido pero no produce una stack de llamada decente). Quité las partes de la stack 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 cuando uso esta función? El solo hecho de comentar esta función hace que las fugas desaparezcan, por lo que no se debe a los parámetros que le paso.

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

Siguiendo con esto, parece que se debe liberar un estado de error (o cadenas).


Cómo desinicializar correctamente OpenSSL

El código para el inicio y el apagado se muestra a continuación (incluido FIPS). Si haces cosas como cargar parámetros de DH, entonces tendrás que limpiarlos también.

El Dr. Henson fue muy útil con su sugerencia de llamar a ERR_remove_state para cada hilo. Consulte Orden de limpieza para evitar memory leaks? 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();
  • CRYPTO_set_locking_callback();

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 progtwig tiene múltiples subprocesos. Consulte la página de comando man Openssl threads (3) .

Creo que puede llamar a SSL_COMP_free_compression_methods en OpenSSL 1.0.2 y SSL_COMP_free_compression_methods posteriores. Eso aborda parte de la queja a continuación. Pero no está disponible en OpenSSL 1.0.1 y abajo.


SSL_COMP_get_compression_methods () …

Sí, esto causa una filtración. Es bien conocido y debido a que ssl_comp_methods se asignan perezosamente pero nunca se liberan. Consulte el problema 2561 de OpenSSL: pérdida de memoria con compresiones integradas de SSL .

Algunos de los desarrolladores de OpenSSL no creen que valga la pena el tiempo para solucionarlo. Consulte Fuga de memoria pequeña en el servidor multiproceso en la lista de correo de OpenSSL.

La siguiente es una de las posiciones del desarrollador:

Una cantidad fija de memoria que no está desasignada y es independiente del número de operaciones realizadas, NO es una pérdida de memoria. Bibliotecas para asignar memoria durante el tiempo de vida del proceso durante la inicialización de una sola vez o el primer uso de una función. Esto es normal.

El seguimiento de 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 a lo sumo una vez? No hay “pérdida de memoria” asociada con tales asignaciones porque la cantidad de memoria extra 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 progtwig, de modo que una pequeña cantidad de “a quién le importa” puede crecer sin límites.

Cuando le informaron sobre el caso de uso alternativo, aquí estaba su respuesta. Supongo que está sugiriendo que Oracle y Java vuelvan a diseñar sus idiomas. O no use OpenSSL en ellos.

La descarga de bibliotecas compartidas generalmente no es segura.

Aquí estaba 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 la estaba usando mucho, y eso fue hace unos años. Las máquinas virtuales de Early Sun no admitían la descarga de bibliotecas, pero esas máquinas virtuales tampoco recogían basura de las clases obsoletas.


Aquí se ssl_comp_methods una sección sobre ssl_comp_methods solucionar los ssl_comp_methods .

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

Para que un progtwig lo haga de forma manual, simplemente agregue una función llamada free_compressions (o similar) y free_compressions al apagar como todos los otros métodos enumerados anteriormente. La función necesita ser exportada.

Para hacerlo automáticamente bajo Linux, se necesita un pequeño truco. __attribute__ ((destructor)) usar una extensión GCC: __attribute__ ((destructor)) .

 /* Add to end of /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, debes hacerlo en DllMain . Pero debes tener cuidado con lo que haces en DllMain y cómo lo haces. Entonces tal vez algo como:

 /* Add to end of /ssl/ssl.h*/ #if !defined(OPENSSL_NO_COMP) && defined(WIN32) __declspec(dllexport) void free_compressions(void); #endof /* Add to end of /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é este hilo está siendo downvoted? El hilo que he vinculado es mucho menos detallado y obtuvo 10 votos ascendentes (más uno de mí). ¿Ustedes se volvieron mucho más estrictos en los últimos años?

Mirando la razón más cercana ( que no se puede hacer en este momento ), se emitió un voto cercano con el motivo:

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

Normalmente eso aplica. Pero en tu caso no es así; y no es evidente para quienes no están familiarizados con el tema. De hecho, podrías escribir un progtwig simple que solo se inicializa y luego unifica la biblioteca y probablemente se filtre …

Como una cuestión de política, el sitio no puede establecer una regla “Proporcione siempre el código relevante a excepción de algunas memory leaks OpenSSL” (que es efectivamente lo que necesitamos para manejar su caso).

Pequeña adición a la publicación jww , si su versión de OpenSSL está comstackda con la biblioteca zlib, entonces debe agregar COMP_zlib_cleanup (); para cerrar la sección. Porque su módulo DSO no se libera 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();