[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Fri Jan 4 14:19:09 UTC 2019


The branch master has been updated
       via  de2debc524e8de89a9e4e8cd890af3882cf1aaab (commit)
       via  41999e7d358c3657a254b34b85fd9e948180529b (commit)
       via  88d57bf83fe32b2c8ceb1264562fdd028de504bf (commit)
       via  d0f2f202c5aa6365d3c13e18a0b9e26837c290a0 (commit)
       via  8f6a5c56c17aa89b80fef73875beec53aef1f2c8 (commit)
       via  660a1e0434eb5eb8548bea3ad35f3821d49c5c15 (commit)
       via  df5228e3b294fc546d0f8ea46e40ac111db58650 (commit)
      from  9c5ef4ea486f675f33592b34775c3e453f60ee69 (commit)


- Log -----------------------------------------------------------------
commit de2debc524e8de89a9e4e8cd890af3882cf1aaab
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Nov 16 17:26:23 2018 +0000

    Support _onexit() in preference to atexit() on Windows
    
    This enables cleanup to happen on DLL unload instead of at process exit.
    
    [extended tests]
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

commit 41999e7d358c3657a254b34b85fd9e948180529b
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Nov 16 14:05:14 2018 +0000

    Introduce a no-pinshared option
    
    This option prevents OpenSSL from pinning itself in memory.
    
    Fixes #7598
    
    [extended tests]
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

commit 88d57bf83fe32b2c8ceb1264562fdd028de504bf
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 15 17:41:06 2018 +0000

    Test atexit handlers
    
    Test that atexit handlers get called properly at process exit, unless we
    have explicitly asked for them not to be.
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

commit d0f2f202c5aa6365d3c13e18a0b9e26837c290a0
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 15 16:59:41 2018 +0000

    Don't link shlibloadtest against libcrypto
    
    The whole point of shlibloadtest is to test dynamically loading and
    unloading the library. If we link shlibloadtest against libcrypto then that
    might mask potential issues.
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

commit 8f6a5c56c17aa89b80fef73875beec53aef1f2c8
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 15 16:27:34 2018 +0000

    Implement OPENSSL_INIT_NO_ATEXIT
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

commit 660a1e0434eb5eb8548bea3ad35f3821d49c5c15
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 20 15:32:55 2018 +0000

    Fix a RUN_ONCE bug
    
    We have a number of instances where there are multiple "init" functions for
    a single CRYPTO_ONCE variable, e.g. to load config automatically or to not
    load config automatically. Unfortunately the RUN_ONCE mechanism was not
    correctly giving the right return value where an alternative init function
    was being used.
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

commit df5228e3b294fc546d0f8ea46e40ac111db58650
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 15 14:50:52 2018 +0000

    Fix shlibloadtest to properly execute the dso_ref test
    
    Reviewed-by: Tim Hudson <tjh at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/7647)

-----------------------------------------------------------------------

Summary of changes:
 Configurations/10-main.conf      |   2 +-
 Configure                        |   1 +
 INSTALL                          |  18 ++++
 crypto/init.c                    | 105 +++++++++++++++----
 doc/man3/OPENSSL_init_crypto.pod |   9 +-
 include/internal/thread_once.h   |  92 +++++++++++++++++
 include/openssl/crypto.h         |   2 +-
 ssl/ssl_init.c                   |   6 +-
 test/build.info                  |   1 -
 test/recipes/90-test_shlibload.t |  45 +++++++--
 test/shlibloadtest.c             | 213 +++++++++++++++++++++++++++------------
 11 files changed, 394 insertions(+), 100 deletions(-)

diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf
index 6506203..21d8345 100644
--- a/Configurations/10-main.conf
+++ b/Configurations/10-main.conf
@@ -651,7 +651,7 @@ my %targets = (
         dso_scheme       => "dlfcn",
         shared_target    => "linux-shared",
         shared_cflag     => "-fPIC",
-        shared_ldflag    => "-Wl,-znodelete",
+        shared_ldflag    => sub { $disabled{pinshared} ? () : "-Wl,-znodelete" },
         shared_extension => ".so.\$(SHLIB_VERSION_NUMBER)",
         enable           => [ "afalgeng" ],
     },
diff --git a/Configure b/Configure
index da09003..7a2be83 100755
--- a/Configure
+++ b/Configure
@@ -374,6 +374,7 @@ my @disablables = (
     "msan",
     "multiblock",
     "nextprotoneg",
+    "pinshared",
     "ocb",
     "ocsp",
     "pic",
diff --git a/INSTALL b/INSTALL
index 049ff21..2fd2235 100644
--- a/INSTALL
+++ b/INSTALL
@@ -416,6 +416,24 @@
   no-pic
                    Don't build with support for Position Independent Code.
 
+  no-pinshared     By default OpenSSL will attempt to stay in memory until the
+                   process exits. This is so that libcrypto and libssl can be
+                   properly cleaned up automatically via an "atexit()" handler.
+                   The handler is registered by libcrypto and cleans up both
+                   libraries. On some platforms the atexit() handler will run on
+                   unload of libcrypto (if it has been dynamically loaded)
+                   rather than at process exit. This option can be used to stop
+                   OpenSSL from attempting to stay in memory until the process
+                   exits. This could lead to crashes if either libcrypto or
+                   libssl have already been unloaded at the point
+                   that the atexit handler is invoked, e.g. on a platform which
+                   calls atexit() on unload of the library, and libssl is
+                   unloaded before libcrypto then a crash is likely to happen.
+                   Applications can suppress running of the atexit() handler at
+                   run time by using the OPENSSL_INIT_NO_ATEXIT option to
+                   OPENSSL_init_crypto(). See the man page for it for further
+                   details.
+
   no-posix-io
                    Don't use POSIX IO capabilities.
 
diff --git a/crypto/init.c b/crypto/init.c
index 1736f2d..5e6be10 100644
--- a/crypto/init.c
+++ b/crypto/init.c
@@ -100,10 +100,6 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_base)
         return 0;
     if ((init_lock = CRYPTO_THREAD_lock_new()) == NULL)
         goto err;
-#ifndef OPENSSL_SYS_UEFI
-    if (atexit(OPENSSL_cleanup) != 0)
-        goto err;
-#endif
     OPENSSL_cpuid_setup();
 
     destructor_key.value = key;
@@ -121,13 +117,53 @@ 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)
+{
+#ifdef OPENSSL_INIT_DEBUG
+    fprintf(stderr, "OPENSSL_INIT: ossl_init_register_atexit()\n");
+#endif
+#ifndef OPENSSL_SYS_UEFI
+# ifdef _WIN32
+    /* 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
+
+    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)
 {
 #ifdef OPENSSL_INIT_DEBUG
     fprintf(stderr, "OPENSSL_INIT: ossl_init_load_crypto_nodelete()\n");
 #endif
-#if !defined(OPENSSL_NO_DSO) && !defined(OPENSSL_USE_NODELETE)
+#if !defined(OPENSSL_NO_DSO) \
+    && !defined(OPENSSL_USE_NODELETE) \
+    && !defined(OPENSSL_NO_PINSHARED)
 # ifdef DSO_WIN32
     {
         HMODULE handle = NULL;
@@ -177,12 +213,6 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_nodelete)
 
 static CRYPTO_ONCE load_crypto_strings = CRYPTO_ONCE_STATIC_INIT;
 static int load_crypto_strings_inited = 0;
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_crypto_strings)
-{
-    /* Do nothing in this case */
-    return 1;
-}
-
 DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings)
 {
     int ret = 1;
@@ -201,6 +231,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings)
     return ret;
 }
 
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_load_crypto_strings,
+                           ossl_init_load_crypto_strings)
+{
+    /* Do nothing in this case */
+    return 1;
+}
+
 static CRYPTO_ONCE add_all_ciphers = CRYPTO_ONCE_STATIC_INIT;
 DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers)
 {
@@ -218,6 +255,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers)
     return 1;
 }
 
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_ciphers,
+                           ossl_init_add_all_ciphers)
+{
+    /* Do nothing */
+    return 1;
+}
+
 static CRYPTO_ONCE add_all_digests = CRYPTO_ONCE_STATIC_INIT;
 DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests)
 {
@@ -235,6 +279,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests)
     return 1;
 }
 
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_digests,
+                           ossl_init_add_all_digests)
+{
+    /* Do nothing */
+    return 1;
+}
+
 static CRYPTO_ONCE add_all_macs = CRYPTO_ONCE_STATIC_INIT;
 DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_macs)
 {
@@ -252,7 +303,7 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_macs)
     return 1;
 }
 
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_add_algs)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_macs, ossl_init_add_all_macs)
 {
     /* Do nothing */
     return 1;
@@ -272,7 +323,7 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_config)
     config_inited = 1;
     return 1;
 }
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_config)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_config, ossl_init_config)
 {
 #ifdef OPENSSL_INIT_DEBUG
     fprintf(stderr,
@@ -606,14 +657,23 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
     if (!RUN_ONCE(&base, ossl_init_base))
         return 0;
 
+    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 (!(opts & OPENSSL_INIT_BASE_ONLY)
             && !RUN_ONCE(&load_crypto_nodelete,
                          ossl_init_load_crypto_nodelete))
         return 0;
 
     if ((opts & OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS)
-            && !RUN_ONCE(&load_crypto_strings,
-                         ossl_init_no_load_crypto_strings))
+            && !RUN_ONCE_ALT(&load_crypto_strings,
+                             ossl_init_no_load_crypto_strings,
+                             ossl_init_load_crypto_strings))
         return 0;
 
     if ((opts & OPENSSL_INIT_LOAD_CRYPTO_STRINGS)
@@ -621,7 +681,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
         return 0;
 
     if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS)
-            && !RUN_ONCE(&add_all_ciphers, ossl_init_no_add_algs))
+            && !RUN_ONCE_ALT(&add_all_ciphers, ossl_init_no_add_all_ciphers,
+                             ossl_init_add_all_ciphers))
         return 0;
 
     if ((opts & OPENSSL_INIT_ADD_ALL_CIPHERS)
@@ -629,7 +690,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
         return 0;
 
     if ((opts & OPENSSL_INIT_NO_ADD_ALL_DIGESTS)
-            && !RUN_ONCE(&add_all_digests, ossl_init_no_add_algs))
+            && !RUN_ONCE_ALT(&add_all_digests, ossl_init_no_add_all_digests,
+                             ossl_init_add_all_digests))
         return 0;
 
     if ((opts & OPENSSL_INIT_ADD_ALL_DIGESTS)
@@ -637,7 +699,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
         return 0;
 
     if ((opts & OPENSSL_INIT_NO_ADD_ALL_MACS)
-            && !RUN_ONCE(&add_all_macs, ossl_init_no_add_algs))
+            && !RUN_ONCE_ALT(&add_all_macs, ossl_init_no_add_all_macs,
+                             ossl_init_add_all_macs))
         return 0;
 
     if ((opts & OPENSSL_INIT_ADD_ALL_MACS)
@@ -649,7 +712,7 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
         return 0;
 
     if ((opts & OPENSSL_INIT_NO_LOAD_CONFIG)
-            && !RUN_ONCE(&config, ossl_init_no_config))
+            && !RUN_ONCE_ALT(&config, ossl_init_no_config, ossl_init_config))
         return 0;
 
     if (opts & OPENSSL_INIT_LOAD_CONFIG) {
@@ -720,7 +783,9 @@ int OPENSSL_atexit(void (*handler)(void))
 {
     OPENSSL_INIT_STOP *newhand;
 
-#if !defined(OPENSSL_NO_DSO) && !defined(OPENSSL_USE_NODELETE)
+#if !defined(OPENSSL_NO_DSO) \
+    && !defined(OPENSSL_USE_NODELETE)\
+    && !defined(OPENSSL_NO_PINSHARED)
     {
         union {
             void *sym;
diff --git a/doc/man3/OPENSSL_init_crypto.pod b/doc/man3/OPENSSL_init_crypto.pod
index ea6b00d..575d0f1 100644
--- a/doc/man3/OPENSSL_init_crypto.pod
+++ b/doc/man3/OPENSSL_init_crypto.pod
@@ -33,7 +33,7 @@ As of version 1.1.0 OpenSSL will automatically allocate all resources that it
 needs so no explicit initialisation is required. Similarly it will also
 automatically deinitialise as required.
 
-However, there way be situations when explicit initialisation is desirable or
+However, there may be situations when explicit initialisation is desirable or
 needed, for example when some non-default initialisation is required. The
 function OPENSSL_init_crypto() can be used for this purpose for
 libcrypto (see also L<OPENSSL_init_ssl(3)> for the libssl
@@ -157,6 +157,13 @@ engines. This not 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
diff --git a/include/internal/thread_once.h b/include/internal/thread_once.h
index c7f0ab2..dcb0c68 100644
--- a/include/internal/thread_once.h
+++ b/include/internal/thread_once.h
@@ -9,6 +9,20 @@
 
 #include <openssl/crypto.h>
 
+/*
+ * DEFINE_RUN_ONCE: Define an initialiser function that should be run exactly
+ * once. It takes no arguments and returns and int result (1 for success or
+ * 0 for failure). Typical usage might be:
+ * 
+ * DEFINE_RUN_ONCE(myinitfunc)
+ * {
+ *     do_some_initialisation();
+ *     if (init_is_successful())
+ *         return 1;
+ *
+ *     return 0;
+ * }
+ */
 #define DEFINE_RUN_ONCE(init)                   \
     static int init(void);                     \
     int init##_ossl_ret_ = 0;                   \
@@ -17,10 +31,30 @@
         init##_ossl_ret_ = init();              \
     }                                           \
     static int init(void)
+
+/*
+ * DECLARE_RUN_ONCE: Declare an initialiser function that should be run exactly
+ * once that has been defined in another file via DEFINE_RUN_ONCE().
+ */
 #define DECLARE_RUN_ONCE(init)                  \
     extern int init##_ossl_ret_;                \
     void init##_ossl_(void);
 
+/*
+ * DEFINE_RUN_ONCE_STATIC: Define an initialiser function that should be run
+ * exactly once. This function will be declared as static within the file. It
+ * takes no arguments and returns and int result (1 for success or 0 for
+ * failure). Typical usage might be:
+ * 
+ * DEFINE_RUN_ONCE_STATIC(myinitfunc)
+ * {
+ *     do_some_initialisation();
+ *     if (init_is_successful())
+ *         return 1;
+ *
+ *     return 0;
+ * }
+ */
 #define DEFINE_RUN_ONCE_STATIC(init)            \
     static int init(void);                     \
     static int init##_ossl_ret_ = 0;            \
@@ -31,6 +65,46 @@
     static int init(void)
 
 /*
+ * DEFINE_RUN_ONCE_STATIC_ALT: Define an alternative initialiser function. This
+ * function will be declared as static within the file. It takes no arguments
+ * and returns and int result (1 for success or 0 for failure). An alternative
+ * initialiser function is expected to be associated with a primary initialiser
+ * function defined via DEFINE_ONCE_STATIC where both functions use the same
+ * CRYPTO_ONCE object to synchronise. Where an alternative initialiser function
+ * is used only one of the primary or the alternative initialiser function will
+ * ever be called - and that function will be called exactly once. Definitition
+ * of an alternative initialiser function MUST occur AFTER the definition of the
+ * primiary initialiser function. 
+ * 
+ * Typical usage might be:
+ * 
+ * DEFINE_RUN_ONCE_STATIC(myinitfunc)
+ * {
+ *     do_some_initialisation();
+ *     if (init_is_successful())
+ *         return 1;
+ *
+ *     return 0;
+ * }
+ * 
+ * DEFINE_RUN_ONCE_STATIC_ALT(myaltinitfunc, myinitfunc)
+ * {
+ *     do_some_alternative_initialisation();
+ *     if (init_is_successful())
+ *         return 1;
+ *
+ *     return 0;
+ * }
+ */
+#define DEFINE_RUN_ONCE_STATIC_ALT(initalt, init) \
+    static int initalt(void);                     \
+    static void initalt##_ossl_(void)             \
+    {                                             \
+        init##_ossl_ret_ = initalt();             \
+    }                                             \
+    static int initalt(void)
+
+/*
  * RUN_ONCE - use CRYPTO_THREAD_run_once, and check if the init succeeded
  * @once: pointer to static object of type CRYPTO_ONCE
  * @init: function name that was previously given to DEFINE_RUN_ONCE,
@@ -43,3 +117,21 @@
  */
 #define RUN_ONCE(once, init)                                            \
     (CRYPTO_THREAD_run_once(once, init##_ossl_) ? init##_ossl_ret_ : 0)
+
+/*
+ * RUN_ONCE_ALT - use CRYPTO_THREAD_run_once, to run an alternative initialiser
+ *                function and check if that initialisation succeeded
+ * @once:    pointer to static object of type CRYPTO_ONCE
+ * @initalt: alternative initialiser function name that was previously given to
+ *           DEFINE_RUN_ONCE_STATIC_ALT.  This function must return 1 for
+ *           success or 0 for failure.
+ * @init:    primary initialiser function name that was previously given to
+ *           DEFINE_RUN_ONCE_STATIC.  This function must return 1 for success or
+ *           0 for failure.
+ *
+ * The return value is 1 on success (*) or 0 in case of error.
+ *
+ * (*) by convention, since the init function must return 1 on success.
+ */
+#define RUN_ONCE_ALT(once, initalt, init)                               \
+    (CRYPTO_THREAD_run_once(once, initalt##_ossl_) ? init##_ossl_ret_ : 0)
diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h
index fc8f32b..4b34812 100644
--- a/include/openssl/crypto.h
+++ b/include/openssl/crypto.h
@@ -379,7 +379,7 @@ int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len);
 /* OPENSSL_INIT_ZLIB                         0x00010000L */
 # define OPENSSL_INIT_ATFORK                 0x00020000L
 /* OPENSSL_INIT_BASE_ONLY                    0x00040000L */
-/* FREE: 0x00080000L */
+# define OPENSSL_INIT_NO_ATEXIT              0x00080000L
 /* OPENSSL_INIT flag range 0x03f00000 reserved for OPENSSL_init_ssl() */
 # define OPENSSL_INIT_NO_ADD_ALL_MACS        0x04000000L
 # define OPENSSL_INIT_ADD_ALL_MACS           0x08000000L
diff --git a/ssl/ssl_init.c b/ssl/ssl_init.c
index 86e6a8d..4ea12fd 100644
--- a/ssl/ssl_init.c
+++ b/ssl/ssl_init.c
@@ -134,7 +134,8 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_ssl_strings)
     return 1;
 }
 
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_ssl_strings)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_load_ssl_strings,
+                           ossl_init_load_ssl_strings)
 {
     /* Do nothing in this case */
     return 1;
@@ -208,7 +209,8 @@ int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS * settings)
         return 0;
 
     if ((opts & OPENSSL_INIT_NO_LOAD_SSL_STRINGS)
-        && !RUN_ONCE(&ssl_strings, ossl_init_no_load_ssl_strings))
+        && !RUN_ONCE_ALT(&ssl_strings, ossl_init_no_load_ssl_strings,
+                         ossl_init_load_ssl_strings))
         return 0;
 
     if ((opts & OPENSSL_INIT_LOAD_SSL_STRINGS)
diff --git a/test/build.info b/test/build.info
index 0227212..962af11 100644
--- a/test/build.info
+++ b/test/build.info
@@ -392,7 +392,6 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=main
     PROGRAMS_NO_INST=shlibloadtest
     SOURCE[shlibloadtest]=shlibloadtest.c
     INCLUDE[shlibloadtest]=../include ../crypto/include
-    DEPEND[shlibloadtest]=libtestutil.a
   ENDIF
 
   IF[{- $disabled{shared} -}]
diff --git a/test/recipes/90-test_shlibload.t b/test/recipes/90-test_shlibload.t
index bb7fab0..ea8aeeb 100644
--- a/test/recipes/90-test_shlibload.t
+++ b/test/recipes/90-test_shlibload.t
@@ -8,6 +8,7 @@
 
 use OpenSSL::Test qw/:DEFAULT bldtop_dir bldtop_file/;
 use OpenSSL::Test::Utils;
+use File::Temp qw(tempfile);
 
 #Load configdata.pm
 
@@ -20,7 +21,7 @@ use configdata;
 plan skip_all => "Test only supported in a shared build" if disabled("shared");
 plan skip_all => "Test is disabled on AIX" if config('target') =~ m|^aix|;
 
-plan tests => 4;
+plan tests => 10;
 
 # When libssl and libcrypto are compiled on Linux with "-rpath", but not
 # "--enable-new-dtags", the RPATH takes precedence over LD_LIBRARY_PATH,
@@ -30,14 +31,31 @@ plan tests => 4;
 my $libcrypto = bldtop_file(shlib('libcrypto'));
 my $libssl = bldtop_file(shlib('libssl'));
 
-ok(run(test(["shlibloadtest", "-crypto_first", $libcrypto, $libssl])),
-   "running shlibloadtest -crypto_first");
-ok(run(test(["shlibloadtest", "-ssl_first", $libcrypto, $libssl])),
-   "running shlibloadtest -ssl_first");
-ok(run(test(["shlibloadtest", "-just_crypto", $libcrypto, $libssl])),
-   "running shlibloadtest -just_crypto");
-ok(run(test(["shlibloadtest", "-dso_ref", $libcrypto, $libssl])),
-   "running shlibloadtest -dso_ref");
+(my $fh, my $filename) = tempfile();
+ok(run(test(["shlibloadtest", "-crypto_first", $libcrypto, $libssl, $filename])),
+   "running shlibloadtest -crypto_first $filename");
+ok(check_atexit($fh));
+unlink $filename;
+($fh, $filename) = tempfile();
+ok(run(test(["shlibloadtest", "-ssl_first", $libcrypto, $libssl, $filename])),
+   "running shlibloadtest -ssl_first $filename");
+ok(check_atexit($fh));
+unlink $filename;
+($fh, $filename) = tempfile();
+ok(run(test(["shlibloadtest", "-just_crypto", $libcrypto, $libssl, $filename])),
+   "running shlibloadtest -just_crypto $filename");
+ok(check_atexit($fh));
+unlink $filename;
+($fh, $filename) = tempfile();
+ok(run(test(["shlibloadtest", "-dso_ref", $libcrypto, $libssl, $filename])),
+   "running shlibloadtest -dso_ref $filename");
+ok(check_atexit($fh));
+unlink $filename;
+($fh, $filename) = tempfile();
+ok(run(test(["shlibloadtest", "-no_atexit", $libcrypto, $libssl, $filename])),
+   "running shlibloadtest -no_atexit $filename");
+ok(!check_atexit($fh));
+unlink $filename;
 
 sub shlib {
     my $lib = shift;
@@ -49,3 +67,12 @@ sub shlib {
     $lib =~ s|\.\$\(SHLIB_VERSION_NUMBER\)|.$config{shlib_version}|;
     return $lib;
 }
+
+sub check_atexit {
+    my $fh = shift;
+    my $data = <$fh>;
+
+    return 1 if (defined $data && $data =~ m/atexit\(\) run/);
+
+    return 0;
+}
diff --git a/test/shlibloadtest.c b/test/shlibloadtest.c
index 4c5d801..6934b89 100644
--- a/test/shlibloadtest.c
+++ b/test/shlibloadtest.c
@@ -14,13 +14,14 @@
 #include <openssl/ssl.h>
 #include <openssl/ossl_typ.h>
 #include "internal/dso_conf.h"
-#include "testutil.h"
 
 typedef void DSO;
 
 typedef const SSL_METHOD * (*TLS_method_t)(void);
 typedef SSL_CTX * (*SSL_CTX_new_t)(const SSL_METHOD *meth);
 typedef void (*SSL_CTX_free_t)(SSL_CTX *);
+typedef int (*OPENSSL_init_crypto_t)(uint64_t, void *);
+typedef int (*OPENSSL_atexit_t)(void (*handler)(void));
 typedef unsigned long (*ERR_get_error_t)(void);
 typedef unsigned long (*OPENSSL_version_major_t)(void);
 typedef unsigned long (*OPENSSL_version_minor_t)(void);
@@ -32,12 +33,14 @@ typedef enum test_types_en {
     CRYPTO_FIRST,
     SSL_FIRST,
     JUST_CRYPTO,
-    DSO_REFTEST
+    DSO_REFTEST,
+    NO_ATEXIT
 } TEST_TYPE;
 
 static TEST_TYPE test_type;
 static const char *path_crypto;
 static const char *path_ssl;
+static const char *path_atexit;
 
 #ifdef DSO_DLFCN
 
@@ -101,6 +104,20 @@ static int shlib_close(SHLIB lib)
 
 #if defined(DSO_DLFCN) || defined(DSO_WIN32)
 
+static int atexit_handler_done = 0;
+
+static void atexit_handler(void)
+{
+    FILE *atexit_file = fopen(path_atexit, "w");
+
+    if (atexit_file == NULL)
+        return;
+
+    fprintf(atexit_file, "atexit() run\n");
+    fclose(atexit_file);
+    atexit_handler_done++;
+}
+
 static int test_lib(void)
 {
     SHLIB ssllib = SHLIB_INIT;
@@ -109,7 +126,7 @@ static int test_lib(void)
     union {
         void (*func)(void);
         SHLIB_SYM sym;
-    } symbols[4];
+    } symbols[5];
     TLS_method_t myTLS_method;
     SSL_CTX_new_t mySSL_CTX_new;
     SSL_CTX_free_t mySSL_CTX_free;
@@ -117,65 +134,100 @@ static int test_lib(void)
     OPENSSL_version_major_t myOPENSSL_version_major;
     OPENSSL_version_minor_t myOPENSSL_version_minor;
     OPENSSL_version_patch_t myOPENSSL_version_patch;
+    OPENSSL_atexit_t myOPENSSL_atexit;
     int result = 0;
 
     switch (test_type) {
     case JUST_CRYPTO:
-        if (!TEST_true(shlib_load(path_crypto, &cryptolib)))
-            goto end;
-        break;
+    case DSO_REFTEST:
+    case NO_ATEXIT:
     case CRYPTO_FIRST:
-        if (!TEST_true(shlib_load(path_crypto, &cryptolib))
-                || !TEST_true(shlib_load(path_ssl, &ssllib)))
+        if (!shlib_load(path_crypto, &cryptolib)) {
+            fprintf(stderr, "Failed to load libcrypto\n");
             goto end;
-        break;
+        }
+        if (test_type != CRYPTO_FIRST)
+            break;
+        /* Fall through */
+
     case SSL_FIRST:
-        if (!TEST_true(shlib_load(path_ssl, &ssllib))
-                || !TEST_true(shlib_load(path_crypto, &cryptolib)))
+        if (!shlib_load(path_ssl, &ssllib)) {
+            fprintf(stderr, "Failed to load libssl\n");
             goto end;
-        break;
-    case DSO_REFTEST:
-        if (!TEST_true(shlib_load(path_crypto, &cryptolib)))
+        }
+        if (test_type != SSL_FIRST)
+            break;
+        if (!shlib_load(path_crypto, &cryptolib)) {
+            fprintf(stderr, "Failed to load libcrypto\n");
             goto end;
+        }
         break;
     }
 
-    if (test_type != JUST_CRYPTO && test_type != DSO_REFTEST) {
-        if (!TEST_true(shlib_sym(ssllib, "TLS_method", &symbols[0].sym))
-                || !TEST_true(shlib_sym(ssllib, "SSL_CTX_new", &symbols[1].sym))
-                || !TEST_true(shlib_sym(ssllib, "SSL_CTX_free", &symbols[2].sym)))
+    if (test_type == NO_ATEXIT) {
+        OPENSSL_init_crypto_t myOPENSSL_init_crypto;
+
+        if (!shlib_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) {
+        if (!shlib_sym(ssllib, "TLS_method", &symbols[0].sym)
+                || !shlib_sym(ssllib, "SSL_CTX_new", &symbols[1].sym)
+                || !shlib_sym(ssllib, "SSL_CTX_free", &symbols[2].sym)) {
+            fprintf(stderr, "Failed to load libssl symbols\n");
             goto end;
+        }
         myTLS_method = (TLS_method_t)symbols[0].func;
         mySSL_CTX_new = (SSL_CTX_new_t)symbols[1].func;
         mySSL_CTX_free = (SSL_CTX_free_t)symbols[2].func;
-        if (!TEST_ptr(ctx = mySSL_CTX_new(myTLS_method())))
+        ctx = mySSL_CTX_new(myTLS_method());
+        if (ctx == NULL) {
+            fprintf(stderr, "Failed to create SSL_CTX\n");
             goto end;
+        }
         mySSL_CTX_free(ctx);
     }
 
-    if (!TEST_true(shlib_sym(cryptolib, "ERR_get_error", &symbols[0].sym))
-           || !TEST_true(shlib_sym(cryptolib, "OPENSSL_version_major",
-                                   &symbols[1].sym))
-           || !TEST_true(shlib_sym(cryptolib, "OPENSSL_version_minor",
-                                   &symbols[2].sym))
-           || !TEST_true(shlib_sym(cryptolib, "OPENSSL_version_patch",
-                                   &symbols[3].sym)))
+    if (!shlib_sym(cryptolib, "ERR_get_error", &symbols[0].sym)
+           || !shlib_sym(cryptolib, "OPENSSL_version_major", &symbols[1].sym)
+           || !shlib_sym(cryptolib, "OPENSSL_version_minor", &symbols[2].sym)
+           || !shlib_sym(cryptolib, "OPENSSL_version_patch", &symbols[3].sym)
+           || !shlib_sym(cryptolib, "OPENSSL_atexit", &symbols[4].sym)) {
+        fprintf(stderr, "Failed to load libcrypto symbols\n");
         goto end;
+    }
     myERR_get_error = (ERR_get_error_t)symbols[0].func;
-    if (!TEST_int_eq(myERR_get_error(), 0))
+    if (myERR_get_error() != 0) {
+        fprintf(stderr, "Unexpected ERR_get_error() response\n");
         goto end;
+    }
 
-    /* Make sure the libraries are a compatible version */
+    /* Library and header version should be identical in this test */
     myOPENSSL_version_major = (OPENSSL_version_major_t)symbols[1].func;
     myOPENSSL_version_minor = (OPENSSL_version_minor_t)symbols[2].func;
     myOPENSSL_version_patch = (OPENSSL_version_patch_t)symbols[3].func;
-    if (!TEST_int_eq(myOPENSSL_version_major(), OPENSSL_VERSION_MAJOR))
-        goto end;
-    if (!TEST_int_ge(myOPENSSL_version_minor(), OPENSSL_VERSION_MINOR))
+    if (myOPENSSL_version_major() != OPENSSL_VERSION_MAJOR
+            || myOPENSSL_version_minor() != OPENSSL_VERSION_MINOR
+            || myOPENSSL_version_patch() != OPENSSL_VERSION_PATCH) {
+        fprintf(stderr, "Invalid library version number\n");
         goto end;
-    if (myOPENSSL_version_minor() == OPENSSL_VERSION_MINOR
-        && !TEST_int_ge(myOPENSSL_version_patch(), OPENSSL_VERSION_PATCH))
+    }
+
+    myOPENSSL_atexit = (OPENSSL_atexit_t)symbols[4].func;
+    if (!myOPENSSL_atexit(atexit_handler)) {
+        fprintf(stderr, "Failed to register atexit handler\n");
         goto end;
+    }
 
     if (test_type == DSO_REFTEST) {
 # ifdef DSO_DLFCN
@@ -190,10 +242,11 @@ static int test_lib(void)
          * will always return an error, because DSO_pathbyaddr() is not
          * implemented there.
          */
-        if (!TEST_true(shlib_sym(cryptolib, "DSO_dsobyaddr", &symbols[0].sym))
-                || !TEST_true(shlib_sym(cryptolib, "DSO_free",
-                                        &symbols[1].sym)))
+        if (!shlib_sym(cryptolib, "DSO_dsobyaddr", &symbols[0].sym)
+                || !shlib_sym(cryptolib, "DSO_free", &symbols[1].sym)) {
+            fprintf(stderr, "Unable to load DSO symbols\n");
             goto end;
+        }
 
         myDSO_dsobyaddr = (DSO_dsobyaddr_t)symbols[0].func;
         myDSO_free = (DSO_free_t)symbols[1].func;
@@ -201,33 +254,44 @@ static int test_lib(void)
         {
             DSO *hndl;
             /* use known symbol from crypto module */
-            if (!TEST_ptr(hndl = myDSO_dsobyaddr((void (*)(void))ERR_get_error, 0)))
+            hndl = myDSO_dsobyaddr((void (*)(void))myERR_get_error, 0);
+            if (hndl == NULL) {
+                fprintf(stderr, "DSO_dsobyaddr() failed\n");
                 goto end;
+            }
             myDSO_free(hndl);
         }
 # endif /* DSO_DLFCN */
     }
 
-    switch (test_type) {
-    case JUST_CRYPTO:
-        if (!TEST_true(shlib_close(cryptolib)))
-            goto end;
-        break;
-    case CRYPTO_FIRST:
-        if (!TEST_true(shlib_close(cryptolib))
-                || !TEST_true(shlib_close(ssllib)))
-            goto end;
-        break;
-    case SSL_FIRST:
-        if (!TEST_true(shlib_close(ssllib))
-                || !TEST_true(shlib_close(cryptolib)))
-            goto end;
-        break;
-    case DSO_REFTEST:
-        if (!TEST_true(shlib_close(cryptolib)))
+    if (!shlib_close(cryptolib)) {
+        fprintf(stderr, "Failed to close libcrypto\n");
+        goto end;
+    }
+
+    if (test_type == CRYPTO_FIRST || test_type == SSL_FIRST) {
+        if (!shlib_close(ssllib)) {
+            fprintf(stderr, "Failed to close libssl\n");
             goto end;
-        break;
+        }
+    }
+
+# if defined(OPENSSL_NO_PINSHARED) \
+    && defined(__GLIBC__) \
+    && defined(__GLIBC_PREREQ) \
+    && defined(OPENSSL_SYS_LINUX)
+#  if __GLIBC_PREREQ(2, 3)
+    /*
+     * If we didn't pin the so then we are hopefully on a platform that supports
+     * 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) {
+        fprintf(stderr, "atexit() handler did not run\n");
+        goto end;
     }
+#  endif
+# endif
 
     result = 1;
 end:
@@ -236,9 +300,21 @@ end:
 #endif
 
 
-int setup_tests(void)
+/*
+ * shlibloadtest should not use the normal test framework because we don't want
+ * it to link against libcrypto (which the framework uses). The point of the
+ * test is to check dynamic loading and unloading of libcrypto/libssl.
+ */
+int main(int argc, char *argv[])
 {
-    const char *p = test_get_argument(0);
+    const char *p;
+
+    if (argc != 5) {
+        fprintf(stderr, "Incorrect number of arguments\n");
+        return 1;
+    }
+
+    p = argv[1];
 
     if (strcmp(p, "-crypto_first") == 0) {
         test_type = CRYPTO_FIRST;
@@ -247,17 +323,24 @@ int setup_tests(void)
     } else if (strcmp(p, "-just_crypto") == 0) {
         test_type = JUST_CRYPTO;
     } else if (strcmp(p, "-dso_ref") == 0) {
-        test_type = JUST_CRYPTO;
+        test_type = DSO_REFTEST;
+    } else if (strcmp(p, "-no_atexit") == 0) {
+        test_type = NO_ATEXIT;
     } else {
-        TEST_error("Unrecognised argument");
-        return 0;
+        fprintf(stderr, "Unrecognised argument\n");
+        return 1;
+    }
+    path_crypto = argv[2];
+    path_ssl = argv[3];
+    path_atexit = argv[4];
+    if (path_crypto == NULL || path_ssl == NULL) {
+        fprintf(stderr, "Invalid libcrypto/libssl path\n");
+        return 1;
     }
-    if (!TEST_ptr(path_crypto = test_get_argument(1))
-            || !TEST_ptr(path_ssl = test_get_argument(2)))
-        return 0;
 
 #if defined(DSO_DLFCN) || defined(DSO_WIN32)
-    ADD_TEST(test_lib);
+    if (!test_lib())
+        return 1;
 #endif
-    return 1;
+    return 0;
 }


More information about the openssl-commits mailing list