[openssl-commits] [openssl] OpenSSL_1_1_1-stable update

Matt Caswell matt at openssl.org
Fri Jan 4 20:26:47 UTC 2019


The branch OpenSSL_1_1_1-stable has been updated
       via  56806f432b6c0cabbc46ebcdf6a9a6009489c0c0 (commit)
       via  6b97cc6ec17586ff9c1d96ab5c3e0b6d829074a8 (commit)
       via  f5f3dfd5efcc1e4073719f788ed4c40f8dc8cf3b (commit)
       via  8ec0a2fef756df65af5494cfb189766015ccb2af (commit)
       via  c2b3db245452f185948b4f767f7e1051b6bd59a7 (commit)
       via  f725fe5b4b6504df08e30f5194d321c3025e2336 (commit)
       via  d6399c85b62e8300ae19cdc243d0b6a2696724bb (commit)
      from  d7389c8261b5c9ca8cfd2b7d3e58f24c9003f650 (commit)


- Log -----------------------------------------------------------------
commit 56806f432b6c0cabbc46ebcdf6a9a6009489c0c0
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/7983)

commit 6b97cc6ec17586ff9c1d96ab5c3e0b6d829074a8
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/7983)

commit f5f3dfd5efcc1e4073719f788ed4c40f8dc8cf3b
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/7983)

commit 8ec0a2fef756df65af5494cfb189766015ccb2af
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/7983)

commit c2b3db245452f185948b4f767f7e1051b6bd59a7
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/7983)

commit f725fe5b4b6504df08e30f5194d321c3025e2336
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/7983)

commit d6399c85b62e8300ae19cdc243d0b6a2696724bb
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/7983)

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

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

diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf
index ac8828e..37e2001 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 b6c9465..7b9501a 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 86412c7..3570dea 100644
--- a/INSTALL
+++ b/INSTALL
@@ -407,6 +407,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 209d1a4..46ba67d 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,7 +279,8 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests)
     return 1;
 }
 
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_add_algs)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_digests,
+                           ossl_init_add_all_digests)
 {
     /* Do nothing */
     return 1;
@@ -255,7 +300,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,
@@ -589,14 +634,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)
@@ -604,7 +658,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)
@@ -612,7 +667,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)
@@ -624,7 +680,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) {
@@ -695,7 +751,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 a259539..b53ab6b 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 2242443..e268a95 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 an 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
+ * primary 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 7e50b1b..d23d9b3 100644
--- a/include/openssl/crypto.h
+++ b/include/openssl/crypto.h
@@ -377,6 +377,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 */
+# define OPENSSL_INIT_NO_ATEXIT              0x00080000L
 /* OPENSSL_INIT flag range 0xfff00000 reserved for OPENSSL_init_ssl() */
 /* Max OPENSSL_INIT flag value is 0x80000000 */
 
diff --git a/ssl/ssl_init.c b/ssl/ssl_init.c
index c0ccb93..9652647 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;
@@ -207,7 +208,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 b6bb711..3ab09ac 100644
--- a/test/build.info
+++ b/test/build.info
@@ -389,7 +389,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 2761d58..f4bca8b 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;
@@ -50,3 +68,12 @@ sub shlib {
              |.$config{shlib_version_number}|x;
     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 53714aa..d88cf88 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_num_t)(void);
 typedef DSO * (*DSO_dsobyaddr_t)(void (*addr)(void), int flags);
@@ -30,12 +31,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
 
@@ -99,6 +102,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;
@@ -113,64 +130,93 @@ static int test_lib(void)
     SSL_CTX_free_t mySSL_CTX_free;
     ERR_get_error_t myERR_get_error;
     OpenSSL_version_num_t myOpenSSL_version_num;
+    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_num",
-                                    &symbols[1].sym)))
+    if (!shlib_sym(cryptolib, "ERR_get_error", &symbols[0].sym)
+           || !shlib_sym(cryptolib, "OpenSSL_version_num", &symbols[1].sym)
+           || !shlib_sym(cryptolib, "OPENSSL_atexit", &symbols[2].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;
+    }
 
-    /*
-     * The bits that COMPATIBILITY_MASK lets through MUST be the same in
-     * the library and in the application.
-     * The bits that are masked away MUST be a larger or equal number in
-     * the library compared to the application.
-     */
-# define COMPATIBILITY_MASK 0xfff00000L
     myOpenSSL_version_num = (OpenSSL_version_num_t)symbols[1].func;
-    if (!TEST_int_eq(myOpenSSL_version_num() & COMPATIBILITY_MASK,
-                     OPENSSL_VERSION_NUMBER & COMPATIBILITY_MASK))
+    if (myOpenSSL_version_num()  != OPENSSL_VERSION_NUMBER) {
+        fprintf(stderr, "Invalid library version number\n");
         goto end;
-    if (!TEST_int_ge(myOpenSSL_version_num() & ~COMPATIBILITY_MASK,
-                     OPENSSL_VERSION_NUMBER & ~COMPATIBILITY_MASK))
+    }
+
+    myOPENSSL_atexit = (OPENSSL_atexit_t)symbols[2].func;
+    if (!myOPENSSL_atexit(atexit_handler)) {
+        fprintf(stderr, "Failed to register atexit handler\n");
         goto end;
+    }
 
     if (test_type == DSO_REFTEST) {
 # ifdef DSO_DLFCN
@@ -185,10 +231,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;
@@ -196,33 +243,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:
@@ -231,9 +289,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;
@@ -242,17 +312,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