]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Make OPENSSL_cleanup() G A
authorBob Beck <beck@openssl.org>
Thu, 22 Jan 2026 19:22:34 +0000 (12:22 -0700)
committerNeil Horman <nhorman@openssl.org>
Thu, 5 Feb 2026 19:19:09 +0000 (14:19 -0500)
(Your choice of G and A words)

This installs a global destructor if we have destructor support.

The global destructor does nothing and immediately returns under
normal operation. If a global flag indicating that global cleanup
is wanted, it does what OPENSSL_cleanup() used to do.

OPENSSL_cleanup() is then modified to set the global flag indicating
that global cleanup is wanted. At this point if we have destructor
support, it immeditely returns. If we do not have destructor support,
it manually calls the destructor function (meaning without destructor
support it does exactly what it used to do).

This ensures that if we have destructor support, the actions of an
OPENSSL_cleanup() requested by an application will only happen
after any subordinate library destructors which could call into
OpenSSL functions have already run.

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Saša Nedvědický <sashan@openssl.org>
MergeDate: Thu Feb  5 19:19:17 2026
(Merged from https://github.com/openssl/openssl/pull/29721)

CHANGES.md
crypto/dllmain.c
crypto/init.c
doc/man3/OPENSSL_init_crypto.pod
include/internal/e_os.h

index b9a544a10c5a967e03238366c65aff0f4b078908..1aef34dc1fb59b8c39bf7183181292097e256983 100644 (file)
@@ -32,6 +32,16 @@ OpenSSL 4.0
 
 ### Changes between 3.6 and 4.0 [xx XXX xxxx]
 
+ * OPENSSL_cleanup() now runs in a global destructor, or not at all by default.
+
+   OpenSSL_cleanup() will no longer by default free global objects when run from
+   an application. Instead it sets a flag for a global destructor to do this after
+   the process exits, and after subordinate libraries using OpenSSL have run their
+   destructors. If destructor support is not available, OpenSSL_cleanup() will do
+   nothing, leaving the global objects to be cleaned up by the Operating System.
+
+   *Bob Beck*
+
  * Added CSHAKE as per [SP 800-185]
 
    *Shane Lontis*
index 78eee3f4c78dc462ce82ceb76cad90dfbd97c7c3..6bc8edf0ccc02434e9a1b07d4117d14a0ba57148 100644 (file)
@@ -37,6 +37,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
         OPENSSL_thread_stop();
         break;
     case DLL_PROCESS_DETACH:
+#if defined(OSSL_DLLMAIN_DESTRUCTOR)
+        ossl_cleanup_destructor();
+#endif /* defined(OSSL_DLLMAIN_DESTRUCTOR) */
         break;
     }
     return TRUE;
index dd866d7e085c1701ad6cde12ed2f3b7b541bd85f..db9f18aee0b921dd013d97f20c08096b4cf1b571 100644 (file)
@@ -48,6 +48,7 @@ static CRYPTO_THREAD_LOCAL in_init_config_local;
 
 static CRYPTO_ONCE base = CRYPTO_ONCE_STATIC_INIT;
 static int base_inited = 0;
+static int do_global_cleanup = 0;
 DEFINE_RUN_ONCE_STATIC(ossl_init_base)
 {
     /* no need to init trace */
@@ -212,7 +213,28 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_async)
     return 1;
 }
 
-void OPENSSL_cleanup(void)
+/*
+ * Global cleanup function. This is optional, and not strictly
+ * necessary to run. Operating systems have successfully been
+ * recovering memory from exiting tasks since the days when I amused
+ * myself by drawing dinosaurs in crayon on used punch cards.
+ *
+ * If we have destructor support, this function is installed and
+ * always run as a global destructor. It only does anything if
+ * someone has called OPENSSL_cleanup() before it is run.
+ *
+ * This ensures that we do the actual cleanup requested by an
+ * OPENSSL_cleanup() only after subordinate library destructors which
+ * may call into OpenSSL have run.
+ *
+ * If we do not have destructor support, then this function is not
+ * normally run, and OPENSSL_cleanup() will do nothing. If we are
+ * compiled with the compile time define of
+ * DO_NOT_SKIP_OPENSSL_CLEANUP, this function will be called
+ * directly from OPENSSL_cleanup() so that cleanup will happen
+ * when OPENSSL_cleanup() is called.
+ */
+void ossl_cleanup_destructor(void)
 {
     /*
      * At some point we should consider looking at this function with a view to
@@ -223,6 +245,10 @@ void OPENSSL_cleanup(void)
     if (!base_inited)
         return;
 
+    /* If we have not been told to clean up, and we are invoked, return */
+    if (!do_global_cleanup)
+        return;
+
     /* Might be explicitly called */
     if (stopped)
         return;
@@ -463,6 +489,18 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
     return 1;
 }
 
+void OPENSSL_cleanup(void)
+{
+    do_global_cleanup = 1;
+#if defined(OSSL_CLEANUP_USING_DESTRUCTOR)
+    return;
+#endif /* defined(OSSL_CLEANUP_USING_DESTRUCTOR) */
+
+#if defined(DO_NOT_SKIP_OPENSSL_CLEANUP)
+    ossl_cleanup_destructor();
+#endif /* defined(DO_NOT_SKIP_OPENSSL_CLEANUP) */
+}
+
 int OPENSSL_atexit(void (*handler)(void))
 {
 #if defined(__TANDEM)
index 830ace5f7c0ad76ca6738df405089f58a62bfad6..f79d1b97dc850930a8b19a601e7d920b0b146f5b 100644 (file)
@@ -135,22 +135,28 @@ OPENSSL_init_crypto(). For example:
  OPENSSL_init_crypto(OPENSSL_INIT_NO_ADD_ALL_CIPHERS
                      | OPENSSL_INIT_NO_ADD_ALL_DIGESTS, NULL);
 
-The OPENSSL_cleanup() function deinitialises OpenSSL (both libcrypto
-and libssl). All resources allocated by OpenSSL are freed.  An application
-using the OpenSSL library may call this function to free library resources
-prior to application exit.  Note that there are some
-use cases in which subordinate libraries may also use OpenSSL and may not
-be finished with their references to it at application exit time (for example,
-if a subordinate library attempts to free an OpenSSL resource from a library
-destructor, calling OPENSSL_cleanup() may result in crashes or other unexpected
-behavior). If this is likely to be a problem then it is recommended that OPENSSL_cleanup()
-not be called, allowing the operating system to reap all library resources on process
-exit.
-
-Note, this may, on some leak detection tools (like valgrind) result in
-reports that indicate reachable memory remains on exit in certain
-configurations.  As these are not formally leaks, it is recommended that
-reachable memory reports be suppressed when running such tools.
+The OPENSSL_cleanup() function requests deinitialization of OpenSSL
+(both libcrypto and libssl). OpenSSL installs a global destructor
+function at initialization time if the toolchain supports this. This
+destructor is run after exit and after subordinate library destructors
+have run. By default the destructor does nothing. If deinitialization
+has been requested by a call to OPENSSL_cleanup(), then the destructor
+frees all global resources allocated by OpenSSL.
+
+If the toolchain building OpenSSL does not support a global
+destructor, by default OPENSSL_cleanup will do nothing, as this is the
+safest option, allowing the operating system to then reap all library
+resources on process exit. Note, this may, on some leak detection
+tools (like valgrind) result in reports that indicate reachable memory
+remains on exit in certain configurations.  As these are not formally
+leaks, it is recommended that reachable memory reports be suppressed
+when running such tools.
+
+If OpenSSL is compiled with the compile time option of DO_NOT_SKIP_OPENSSL_CLEANUP
+OPENSSL_cleanup() will deinitialize the library immediately when called. This
+is not normally recommended unless you are certain that the global resources
+will not be used by something after the point at which OPENSSL_cleanup() is
+called.
 
 Once OPENSSL_cleanup() has been called the library cannot be reinitialised.
 Attempts to call OPENSSL_init_crypto() will fail and an ERR_R_INIT_FAIL error
index a237005e7e8fd673802ff8e88f1714bc4a384422..1cec9d787577d084cec498b24a595922226dd84a 100644 (file)
@@ -366,3 +366,48 @@ typedef _locale_t locale_t;
 #endif
 
 #endif
+
+/*
+ * Can we use a global destructor?  We can use a global destructor via
+ * __attribute__ on anything like a modern gcc/clang.  We can also use
+ * it via dllmain on anything win32/win64.
+ *
+ * Older things may not do this.
+ * The assumption here is then if you don't have destructor support,
+ * it is safe to call OPENSSL_cleanup before an application exits
+ * because no library it is linked with will run code in a destructor
+ * that will call into OpenSSL after exit() happens.
+ *
+ */
+#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WIN64)
+#define OSSL_CLEANUP_USING_DESTRUCTOR
+#define OSSL_DLLMAIN_DESTRUCTOR
+/*
+ * destructor will be installed in libcrypto's dllmain.c
+ * This means effectively anything not win16 or dos will handle
+ * this.
+ */
+void ossl_cleanup_destructor(void);
+#else
+#if defined(__has_attribute)
+#if __has_attribute(destructor)
+/*
+ * This seems to have been a thing with any gcc or clang since the
+ * early 2000's. So this could pretty much instead be just unconditional
+ * on __GNUC__ or __clang__.
+ */
+#define OSSL_CLEANUP_USING_DESTRUCTOR
+/* destructor is installed by compiler */
+void ossl_cleanup_destructor(void) __attribute__((destructor));
+#else
+/* We are not using a destructor */
+/*
+ * So we are on something that is not close to Windows or being
+ * compiled with a modern GCC/Clang derivative. either way
+ * this probably means something like a toolchain that is
+ * more than 20 years old.
+ */
+void ossl_cleanup_destructor(void);
+#endif /* defined (__has_attribute(destructor) */
+#endif /* defined (__has_attribute) */
+#endif /* defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WIN64) */