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)
This commit is contained in:
Alexandr Nedvedicky
2025-11-24 17:05:26 +01:00
committed by Neil Horman
parent 380ff14485
commit 31659fe326
12 changed files with 41 additions and 173 deletions
+8
View File
@@ -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.
+1
View File
@@ -967,6 +967,7 @@ cleanup:
EVP_MAC_CTX_free(ctx);
OPENSSL_free(read_buffer);
free_config_and_unload(conf);
OPENSSL_cleanup();
return ret;
}
+1
View File
@@ -372,6 +372,7 @@ end:
#ifndef OPENSSL_NO_SECURE_MEMORY
CRYPTO_secure_malloc_done();
#endif
OPENSSL_cleanup();
EXIT(ret);
}
+6 -132
View File
@@ -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(&register_atexit, ossl_init_no_register_atexit,
ossl_init_register_atexit))
return 0;
} else if (!RUN_ONCE(&register_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();
#if defined(__TANDEM)
return 0;
#else
return atexit(handler) == 0;
#endif
}
#endif
if ((newhand = OPENSSL_malloc(sizeof(*newhand))) == NULL)
return 0;
newhand->handler = handler;
newhand->next = stop_handlers;
stop_handlers = newhand;
return 1;
}
+5
View File
@@ -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)
+1 -12
View File
@@ -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
+1 -1
View File
@@ -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 */
+6
View File
@@ -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;
+3
View File
@@ -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();
}
+6
View File
@@ -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()) {
+1 -7
View File
@@ -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;
+2 -21
View File
@@ -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;