From: Alexandr Nedvedicky Date: Mon, 24 Nov 2025 16:05:26 +0000 (+0100) Subject: Introduce OPENSSL_ATEXIT_CLEANUP env. variable. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=31659fe32673a6bd66abf3f8a7d803e81c6ffeed;p=thirdparty%2Fopenssl.git Introduce OPENSSL_ATEXIT_CLEANUP env. variable. libcrypto does not arm OPENSSL_cleanup() function as atexit(3) handler by default. If application/user wants libcrypto to install OPENSSL_cleanup() as atexit handler, then OPENSSL_ATEXIT_CLEANUP env. variable must be set. If platform's libc does not provide atexit(3), then OPENSSL_ATEXIT_CLEANUP has no effect. The OPENSSL_atexit() is wrapper of atexit(3) provided by libc now. Reviewed-by: Neil Horman Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/29385) --- diff --git a/CHANGES.md b/CHANGES.md index 0ca5ba53989..f17d0947324 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -36,11 +36,19 @@ OpenSSL 4.0 *Norbert Pocs* + * libcrypto no longer arms OPENSSL_cleanup() as atexit(3) handler by default. + Memory leak detectors now report there is allocated and reachable memory + at application exit. To avoid such leak detection the application must + call OPENSSL_cleanup() before main() exits. + + *Alexandr Nedvedicky* + * The crypto-mdebug-backtrace configuration option has been entirely removed. The option has been a no-op since 1.0.2. *Neil Horman* + * Removed extra leading '00:' when printing key data such as an RSA modulus in hexadecimal format where the first (most significant) byte is >= 0x80. This had been added artificially to resemble ASN.1 DER encoding internals. diff --git a/apps/fipsinstall.c b/apps/fipsinstall.c index ea54a00cffc..10114cb1d83 100644 --- a/apps/fipsinstall.c +++ b/apps/fipsinstall.c @@ -967,6 +967,7 @@ cleanup: EVP_MAC_CTX_free(ctx); OPENSSL_free(read_buffer); free_config_and_unload(conf); + OPENSSL_cleanup(); return ret; } diff --git a/apps/openssl.c b/apps/openssl.c index 61623086f7d..2d587e22498 100644 --- a/apps/openssl.c +++ b/apps/openssl.c @@ -372,6 +372,7 @@ end: #ifndef OPENSSL_NO_SECURE_MEMORY CRYPTO_secure_malloc_done(); #endif + OPENSSL_cleanup(); EXIT(ret); } diff --git a/crypto/init.c b/crypto/init.c index 457ce4ca69a..3049c48a5e6 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -35,13 +35,6 @@ static int stopped = 0; static uint64_t optsdone = 0; -typedef struct ossl_init_stop_st OPENSSL_INIT_STOP; -struct ossl_init_stop_st { - void (*handler)(void); - OPENSSL_INIT_STOP *next; -}; - -static OPENSSL_INIT_STOP *stop_handlers = NULL; /* Guards access to the optsdone variable on platforms without atomics */ static CRYPTO_RWLOCK *optsdone_lock = NULL; /* Guards simultaneous INIT_LOAD_CONFIG calls with non-NULL settings */ @@ -84,45 +77,6 @@ err: return 0; } -static CRYPTO_ONCE register_atexit = CRYPTO_ONCE_STATIC_INIT; -#if !defined(OPENSSL_SYS_UEFI) && defined(_WIN32) -static int win32atexit(void) -{ - OPENSSL_cleanup(); - return 0; -} -#endif - -DEFINE_RUN_ONCE_STATIC(ossl_init_register_atexit) -{ -#ifndef OPENSSL_NO_ATEXIT -#ifdef OPENSSL_INIT_DEBUG - fprintf(stderr, "OPENSSL_INIT: ossl_init_register_atexit()\n"); -#endif -#ifndef OPENSSL_SYS_UEFI -#if defined(_WIN32) && !defined(__BORLANDC__) - /* We use _onexit() in preference because it gets called on DLL unload */ - if (_onexit(win32atexit) == NULL) - return 0; -#else - if (atexit(OPENSSL_cleanup) != 0) - return 0; -#endif -#endif -#endif - - return 1; -} - -DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_register_atexit, - ossl_init_register_atexit) -{ -#ifdef OPENSSL_INIT_DEBUG - fprintf(stderr, "OPENSSL_INIT: ossl_init_no_register_atexit ok!\n"); -#endif - /* Do nothing in this case */ - return 1; -} static CRYPTO_ONCE load_crypto_nodelete = CRYPTO_ONCE_STATIC_INIT; DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_nodelete) @@ -150,7 +104,7 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_nodelete) #elif !defined(DSO_NONE) /* * Deliberately leak a reference to ourselves. This will force the library - * to remain loaded until the atexit() handler is run at process exit. + * to remain loaded until the OPENSSL_cleanup() is called. */ { DSO *dso; @@ -308,8 +262,6 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_async) void OPENSSL_cleanup(void) { - OPENSSL_INIT_STOP *currhandler, *lasthandler; - /* * At some point we should consider looking at this function with a view to * moving most/all of this into onfree handlers in OSSL_LIB_CTX. @@ -319,7 +271,7 @@ void OPENSSL_cleanup(void) if (!base_inited) return; - /* Might be explicitly called and also by atexit */ + /* Might be explicitly called a*/ if (stopped) return; stopped = 1; @@ -330,15 +282,6 @@ void OPENSSL_cleanup(void) */ OPENSSL_thread_stop(); - currhandler = stop_handlers; - while (currhandler != NULL) { - currhandler->handler(); - lasthandler = currhandler; - currhandler = currhandler->next; - OPENSSL_free(lasthandler); - } - stop_handlers = NULL; - CRYPTO_THREAD_lock_free(optsdone_lock); optsdone_lock = NULL; CRYPTO_THREAD_lock_free(init_lock); @@ -486,20 +429,6 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) return 1; } - /* - * Now we don't always set up exit handlers, the INIT_BASE_ONLY calls - * should not have the side-effect of setting up exit handlers, and - * therefore, this code block is below the INIT_BASE_ONLY-conditioned early - * return above. - */ - if ((opts & OPENSSL_INIT_NO_ATEXIT) != 0) { - if (!RUN_ONCE_ALT(®ister_atexit, ossl_init_no_register_atexit, - ossl_init_register_atexit)) - return 0; - } else if (!RUN_ONCE(®ister_atexit, ossl_init_register_atexit)) { - return 0; - } - if (!RUN_ONCE(&load_crypto_nodelete, ossl_init_load_crypto_nodelete)) return 0; @@ -586,64 +515,9 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) int OPENSSL_atexit(void (*handler)(void)) { - OPENSSL_INIT_STOP *newhand; - -#if !defined(OPENSSL_USE_NODELETE) \ - && !defined(OPENSSL_NO_PINSHARED) - { -#if defined(DSO_WIN32) && !defined(_WIN32_WCE) - HMODULE handle = NULL; - BOOL ret; - union { - void *sym; - void (*func)(void); - } handlersym; - - handlersym.func = handler; - - /* - * We don't use the DSO route for WIN32 because there is a better - * way - */ - ret = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS - | GET_MODULE_HANDLE_EX_FLAG_PIN, - handlersym.sym, &handle); - - if (!ret) - return 0; -#elif !defined(DSO_NONE) - /* - * Deliberately leak a reference to the handler. This will force the - * library/code containing the handler to remain loaded until we run the - * atexit handler. If -znodelete has been used then this is - * unnecessary. - */ - DSO *dso = NULL; - union { - void *sym; - void (*func)(void); - } handlersym; - - handlersym.func = handler; - - ERR_set_mark(); - dso = DSO_dsobyaddr(handlersym.sym, DSO_FLAG_NO_UNLOAD_ON_FREE); - /* See same code above in ossl_init_base() for an explanation. */ - OSSL_TRACE1(INIT, - "atexit: obtained DSO reference? %s\n", - (dso == NULL ? "No!" : "Yes.")); - DSO_free(dso); - ERR_pop_to_mark(); -#endif - } +#if defined(__TANDEM) + return 0; +#else + return atexit(handler) == 0; #endif - - if ((newhand = OPENSSL_malloc(sizeof(*newhand))) == NULL) - return 0; - - newhand->handler = handler; - newhand->next = stop_handlers; - stop_handlers = newhand; - - return 1; } diff --git a/crypto/initthread.c b/crypto/initthread.c index 1e1b9e69db5..d408ea9ca16 100644 --- a/crypto/initthread.c +++ b/crypto/initthread.c @@ -326,7 +326,12 @@ err: void ossl_thread_event_ctx_free(OSSL_LIB_CTX *ctx) { + THREAD_EVENT_HANDLER **hands; + + hands = (THREAD_EVENT_HANDLER **)CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx); CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx, NULL); + + OPENSSL_free(hands); } static void ossl_arg_thread_stop(void *arg) diff --git a/doc/man3/OPENSSL_init_crypto.pod b/doc/man3/OPENSSL_init_crypto.pod index 3ef6aba822a..0740c219d2d 100644 --- a/doc/man3/OPENSSL_init_crypto.pod +++ b/doc/man3/OPENSSL_init_crypto.pod @@ -124,13 +124,6 @@ sub-library (see L). This is a default option. With this option the library will register its fork handlers. See OPENSSL_fork_prepare(3) for details. -=item OPENSSL_INIT_NO_ATEXIT - -By default OpenSSL will attempt to clean itself up when the process exits via an -"atexit" handler. Using this option suppresses that behaviour. This means that -the application will have to clean up OpenSSL explicitly using -OPENSSL_cleanup(). - =back Multiple options may be combined together in a single call to @@ -158,11 +151,7 @@ OpenSSL error strings will not be available, only an error code. This code can be put through the openssl errstr command line application to produce a human readable error (see L). -The OPENSSL_atexit() function enables the registration of a -function to be called during OPENSSL_cleanup(). Stop handlers are -called after deinitialisation of resources local to a thread, but before other -process wide resources are freed. In the event that multiple stop handlers are -registered, no guarantees are made about the order of execution. +The OPENSSL_atexit() is a wrapper on atexit(3) provided by platform's libc. The OPENSSL_thread_stop_ex() function deallocates resources associated with the current thread for the given OSSL_LIB_CTX B. The B parameter diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in index c9dc3706f37..93419850265 100644 --- a/include/openssl/crypto.h.in +++ b/include/openssl/crypto.h.in @@ -490,8 +490,8 @@ int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len); /* FREE: 0x00010000L */ #define OPENSSL_INIT_ATFORK 0x00020000L /* OPENSSL_INIT_BASE_ONLY 0x00040000L */ -#define OPENSSL_INIT_NO_ATEXIT 0x00080000L /* OPENSSL_INIT flag range 0x03f00000 reserved for OPENSSL_init_ssl() */ +/* FREE: 0x00080000L */ /* FREE: 0x04000000L */ /* FREE: 0x08000000L */ /* FREE: 0x10000000L */ diff --git a/test/defltfips_test.c b/test/defltfips_test.c index 16d834b020c..b7e85211c4e 100644 --- a/test/defltfips_test.c +++ b/test/defltfips_test.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include "testutil.h" @@ -68,6 +69,11 @@ static int test_is_fips_enabled(void) return 1; } +void cleanup_tests(void) +{ + OPENSSL_cleanup(); +} + int setup_tests(void) { size_t argc; diff --git a/test/endecode_test.c b/test/endecode_test.c index 6081ef5d0be..58fd12473b5 100644 --- a/test/endecode_test.c +++ b/test/endecode_test.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -1767,4 +1768,6 @@ void cleanup_tests(void) OSSL_PROVIDER_unload(keyprov); OSSL_LIB_CTX_free(testctx); OSSL_LIB_CTX_free(keyctx); + + OPENSSL_cleanup(); } diff --git a/test/rand_test.c b/test/rand_test.c index 0fbd89542f2..0ac097cb34a 100644 --- a/test/rand_test.c +++ b/test/rand_test.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include @@ -275,6 +276,11 @@ err: return res; } +void cleanup_tests(void) +{ + OPENSSL_cleanup(); +} + int setup_tests(void) { if (!test_skip_common_options()) { diff --git a/test/recipes/90-test_shlibload.t b/test/recipes/90-test_shlibload.t index 67afff607e0..7a717dd92a5 100644 --- a/test/recipes/90-test_shlibload.t +++ b/test/recipes/90-test_shlibload.t @@ -25,7 +25,7 @@ plan skip_all => "Test only supported in a dso build" if disabled("dso"); plan skip_all => "Test is disabled in an address sanitizer build" unless disabled("asan"); plan skip_all => "Test is disabled in no-atexit build" if disabled("atexit"); -plan tests => 10; +plan tests => 8; my $libcrypto = platform->sharedlib('libcrypto'); my $libssl = platform->sharedlib('libssl'); @@ -55,12 +55,6 @@ ok(run(test(["shlibloadtest", "-dso_ref", $libcrypto, $libssl, $atexit_outfile]) "running shlibloadtest -dso_ref $atexit_outfile"); ok(check_atexit($atexit_outfile)); -$atexit_outfile = 'atexit-noatexit.txt'; -1 while unlink $atexit_outfile; -ok(run(test(["shlibloadtest", "-no_atexit", $libcrypto, $libssl, $atexit_outfile])), - "running shlibloadtest -no_atexit $atexit_outfile"); -ok(!check_atexit($atexit_outfile)); - sub check_atexit { my $filename = shift; diff --git a/test/shlibloadtest.c b/test/shlibloadtest.c index 034780ea55c..5fa37415a57 100644 --- a/test/shlibloadtest.c +++ b/test/shlibloadtest.c @@ -34,7 +34,6 @@ typedef enum test_types_en { SSL_FIRST, JUST_CRYPTO, DSO_REFTEST, - NO_ATEXIT } TEST_TYPE; static TEST_TYPE test_type; @@ -80,7 +79,6 @@ static int test_lib(void) switch (test_type) { case JUST_CRYPTO: case DSO_REFTEST: - case NO_ATEXIT: case CRYPTO_FIRST: if (!sd_load(path_crypto, &cryptolib, SD_SHLIB)) { fprintf(stderr, "Failed to load libcrypto\n"); @@ -104,23 +102,8 @@ static int test_lib(void) break; } - if (test_type == NO_ATEXIT) { - OPENSSL_init_crypto_t myOPENSSL_init_crypto; - - if (!sd_sym(cryptolib, "OPENSSL_init_crypto", &symbols[0].sym)) { - fprintf(stderr, "Failed to load OPENSSL_init_crypto symbol\n"); - goto end; - } - myOPENSSL_init_crypto = (OPENSSL_init_crypto_t)symbols[0].func; - if (!myOPENSSL_init_crypto(OPENSSL_INIT_NO_ATEXIT, NULL)) { - fprintf(stderr, "Failed to initialise libcrypto\n"); - goto end; - } - } - if (test_type != JUST_CRYPTO - && test_type != DSO_REFTEST - && test_type != NO_ATEXIT) { + && test_type != DSO_REFTEST) { if (!sd_sym(ssllib, "TLS_method", &symbols[0].sym) || !sd_sym(ssllib, "SSL_CTX_new", &symbols[1].sym) || !sd_sym(ssllib, "SSL_CTX_free", &symbols[2].sym)) { @@ -228,7 +211,7 @@ static int test_lib(void) * running atexit() on so unload. If not we might crash. We know this is * true on linux since glibc 2.2.3 */ - if (test_type != NO_ATEXIT && atexit_handler_done != 1) { + if (atexit_handler_done != 1) { fprintf(stderr, "atexit() handler did not run\n"); goto end; } @@ -269,8 +252,6 @@ int main(int argc, char *argv[]) test_type = JUST_CRYPTO; } else if (strcmp(p, "-dso_ref") == 0) { test_type = DSO_REFTEST; - } else if (strcmp(p, "-no_atexit") == 0) { - test_type = NO_ATEXIT; } else { fprintf(stderr, "Unrecognised argument\n"); return 1;