Commit 31659fe326 for openssl.org
commit 31659fe32673a6bd66abf3f8a7d803e81c6ffeed
Author: Alexandr Nedvedicky <sashan@openssl.org>
Date: Mon Nov 24 17:05:26 2025 +0100
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 <nhorman@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/29385)
diff --git a/CHANGES.md b/CHANGES.md
index 0ca5ba5398..f17d094732 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 ea54a00cff..10114cb1d8 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 61623086f7..2d587e2249 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 457ce4ca69..3049c48a5e 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 1e1b9e69db..d408ea9ca1 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 3ef6aba822..0740c219d2 100644
--- a/doc/man3/OPENSSL_init_crypto.pod
+++ b/doc/man3/OPENSSL_init_crypto.pod
@@ -124,13 +124,6 @@ sub-library (see L<ASYNC_start_job(3)>). 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<openssl-errstr(1)>).
-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<ctx>. The B<ctx> parameter
diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in
index c9dc3706f3..9341985026 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 16d834b020..b7e85211c4 100644
--- a/test/defltfips_test.c
+++ b/test/defltfips_test.c
@@ -8,6 +8,7 @@
*/
#include <string.h>
+#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/provider.h>
#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 6081ef5d0b..58fd12473b 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -9,6 +9,7 @@
#include <string.h>
#include <openssl/core_dispatch.h>
+#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
@@ -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 0fbd89542f..0ac097cb34 100644
--- a/test/rand_test.c
+++ b/test/rand_test.c
@@ -7,6 +7,7 @@
* https://www.openssl.org/source/license.html
*/
+#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
@@ -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 67afff607e..7a717dd92a 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 034780ea55..5fa37415a5 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;