Openssl::Ssl_Library_Init() Memory Leak

OpenSSL::SSL_library_init() memory leak

As I understand all the memory which is allocated during SSL_library_init() and SSL_load_error_strings() are stored in global variables and so it comes under the category of "Memory in Use" rather under the category of Memory leak as the memory is still accessible when the program is dying out.

One suggestion is that ERR_remove_state(0) must be called in each thread where SSL is used, because when you call the ERR_remove_state with argument 0, it just clears the error state for the current thread. Other calls appears good to me. If you could post, "two leaks" which are still being displayed by VLD, I can check.

How to properly uninitialize OpenSSL

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

Latching on to this, it looks like some error state (or strings) needs to be free'd.


How to properly uninitialize OpenSSL

The code for startup and shutdown is shown below (including FIPS). If you do things like load DH parameters, then you will need to clean them up, too.

Dr. Henson was very helpful with his suggestion of calling ERR_remove_state for each thread. See Order of Cleanup to avoid memory leaks? on the OpenSSL mailing list.

Startup

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

Shutdown

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

And, for each thread:

  • ERR_remove_state();

You only need CRYPTO_set_id_callback and CRYPTO_set_locking_callback if your program is multi-threaded. See the Openssl threads(3) man page.

I believe you can call SSL_COMP_free_compression_methods in OpenSSL 1.0.2 and above. That addresses some of the complaint below. But its not available in OpenSSL 1.0.1 and below.


SSL_COMP_get_compression_methods()...

Yeah, this causes a leak. Its well known and due to ssl_comp_methods being lazily allocated but never freed. See OpenSSL Issue 2561: Memory leak with SSL built-in compressions.

Some of the OpenSSL devs don't feel its worth their time to fix it. See Small memory leak on multithreaded server on the OpenSSL mailing list.

The following is one of the dev's position on it:

A fixed amount of memory that is not deallocated and is independent of
the number of operations performed, is NOT a memory leak. Libraries
to allocate memory for the lifetime of the process during one time
initialization or first use of a function. This is normal.

Tracking this down is a waste of time IMHO.

And here's another thread about that particular leak: Preferred way to free ssl_comp_methods?. And here's that same dev's response:

Why is anyone obsessed about freeing memory that is assigned to static
pointers at most once. There's no "memory leak" associated with such
allocations because the amount of extra memory used is fixed.

What he failed to realize is Java and .Net will load/unload the library many times during a program's lifecycle, so that small amount of "who cares" can grow unbounded.

When he was told about the alternate use case, here was his reply. I guess, he is suggesting that Oracle and Java re-architect their languages. Or don't use OpenSSL in them.

Unloading of shared libraries is generally unsafe.

Here was the response of one of the folks who maintain a Java VM:

As the maintainer of an "alternative" JavaVM I can confirm that we
absolutely had to support library unloading because one customer was
using it heavily - and that was quite a few years ago. Early Sun VMs
didn't support library unloading, but then those VMs also did not
garbage-collect obsolete classes either.


Here a section on fixing the ssl_comp_methods leak.

In all cases, you will need to add the patch described below.

For a program to do it manually, just add a function named free_compressions (or similar) and call it on shutdown like all the other methods listed above. The function needs to be exported.

To do it automatically under Linux, its takes a little trickery. You have to use a GCC extension: __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

To do it automatically under Windows, you have to do it in DllMain. But you have to be careful about what you do in DllMain, and how you do it. So maybe something like:

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

-----

Why is this thread being downvoted? The thread I linked is a lot less detailed and it got 10 upvotes (plus one from me). Did you guys become a lot more strict in the last few years?

Looking at the close reason (which you can't do at the moment), the close vote was cast with the reason:

Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself.

Normally that applies. But in your case it does not; and its not readily apparent to those not familiar with the issue. In fact, you could write a simple program that just initializes and then unitializes the library and it will probably leak...

As a matter of policy, the site can't make a rule "Always provide relevant code except for some OpenSSL memory leaks" (which is effectively what we need to handle your case).

Memory leak in OpenSSL?

  1. What @iharob wrote in his answer and,

  2. You used EVP_MD_CTX_init() but did not free the data it allocated using EVP_MD_CTX_cleanup(&ctx) or - depending on your openssl version - EVP_MD_CTX_free(&ctx).

    And if you compile with debug info (using -g in gcc/clang) valgrind would show you the source code lines responsible for the leaks.

  3. You could also use EVP_MD_CTX_create() instead of using a static EVP_MD_CTX.

Here is the updated code:

#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>

int main() {
const unsigned char key[] =
"-----BEGIN PUBLIC KEY-----\n"
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALulMfYFyX1kSm7oUxZyCWWhrBBcWrRA\n"
"V7LSz1PzMPxoxG3KS8H7PRKFkIk42yM8/vhobmcCmj7UM5572wWch50CAwEAAQ==\n"
"-----END PUBLIC KEY-----\n";
unsigned int key_len = sizeof(key) - 1;

const unsigned char data[] = { 0x6d,0x65,0x73,0x65,0x0a };
unsigned int data_len = sizeof(data);

const unsigned char sig[] = {
0xa9,0x29,0x81,0x07,0x8c,0xeb,0xf0,0x1b,0x2a,0x31,0xe5,0x60,
0x94,0x8a,0x47,0x94,0x3a,0x8f,0x6b,0x4e,0x85,0xb8,0xe7,0xe5,
0x4a,0x6c,0x56,0x46,0xd1,0x80,0x15,0x57,0xce,0xcb,0x0a,0x3a,
0x67,0x15,0xed,0x68,0x03,0x58,0x99,0xa4,0x73,0x61,0xe3,0x30,
0x85,0xff,0x89,0x7e,0x32,0xef,0x16,0xec,0x23,0x7f,0x14,0xde,
0xbf,0x53,0xe0,0x3a
};
unsigned int sig_len = sizeof(sig);

int ret = 0;

EVP_PKEY* evp_pubkey = NULL;
RSA* rsa_pubkey = NULL;
EVP_MD_CTX* evp_ctx;
BIO* bufio = NULL;

if (!(bufio = BIO_new_mem_buf((void*)key, key_len))) {
fprintf(stderr, "BIO not created.\n");
ret = 1;
goto out;
}

if (!(evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL))) {
fprintf(stderr, "evp_pubkey not created.\n");
ret = 2;
goto out;
}

if (!(evp_ctx = EVP_MD_CTX_create())) {
fprintf(stderr, "ctx not created.\n");
ret = 3;
goto out;
}

if (!EVP_VerifyInit(evp_ctx, EVP_sha256())) {
fprintf(stderr, "EVP_SignInit: failed.\n");
ret = 4;
goto out;
}

if (!EVP_VerifyUpdate(evp_ctx, data, data_len)) {
fprintf(stderr, "EVP_SignUpdate: failed.\n");
ret = 5;
goto out;
}

if (!EVP_VerifyFinal(evp_ctx, sig, sig_len, evp_pubkey)) {
fprintf(stderr, "EVP_VerifyFinal: failed.\n");
ret = 6;
goto out;
}

out:
if (evp_ctx)
EVP_MD_CTX_destroy(evp_ctx);

if (evp_pubkey)
EVP_PKEY_free(evp_pubkey);

if (bufio)
BIO_free(bufio);

return 0;
}

RSA_new and RSA_generate_key_ex make memory leakage

I tried your program (after fixing typos such as RAS_generate_key_ex -> RSA_generate_key_ex and EVP_PKEY pkey = EVP_PKEY_new() -> EVP_PKEY* pkey = EVP_PKEY_new()).

I had now this source:

#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>

int main() {
BIGNUM e;
BN_init(&e);
BN_set_word(&e, 17);
RSA *rsa = RSA_new();
RSA_generate_key_ex(rsa, 1024, &e, NULL);
EVP_PKEY* pkey = EVP_PKEY_new();
EVP_PKEY_set1_RSA(pkey, rsa);
RSA_free(rsa);
BN_free(&e);
//MINE_COPY_KEY(pkey); // I copy the pkey to other location at here //
EVP_PKEY_free(pkey);
return 0;
}

Valgrind says:

==18061== 
==18061== LEAK SUMMARY:
==18061== definitely lost: 0 bytes in 0 blocks
==18061== indirectly lost: 0 bytes in 0 blocks
==18061== possibly lost: 0 bytes in 0 blocks
==18061== still reachable: 220 bytes in 6 blocks
==18061== suppressed: 0 bytes in 0 blocks
==18061== Reachable blocks (those to which a pointer was found) are not shown.
==18061== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==18061==
==18061== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==18061== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Adding CRYPTO_cleanup_all_ex_data(); at the end of the program makes Valgring happy :-)

==18212== HEAP SUMMARY:
==18212== in use at exit: 0 bytes in 0 blocks
==18212== total heap usage: 457 allocs, 457 frees, 31,748 bytes allocated
==18212==
==18212== All heap blocks were freed -- no leaks are possible
==18212==
==18212== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==18212== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

See: https://stackoverflow.com/a/21533000/6267288



Related Topics



Leave a reply



Submit