]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - crypto/init.c
Always check CRYPTO_LOCK_{read,write}_lock
[thirdparty/openssl.git] / crypto / init.c
index f7c7d59f5582d396620fe76ae16692f1cfe63c7a..3b6a16a76d255471a95894c3a387aca6fac05b86 100644 (file)
@@ -34,6 +34,7 @@
 #include <openssl/trace.h>
 
 static int stopped = 0;
+static uint64_t optsdone = 0;
 
 typedef struct ossl_init_stop_st OPENSSL_INIT_STOP;
 struct ossl_init_stop_st {
@@ -233,8 +234,16 @@ static CRYPTO_ONCE config = CRYPTO_ONCE_STATIC_INIT;
 static int config_inited = 0;
 static const OPENSSL_INIT_SETTINGS *conf_settings = NULL;
 DEFINE_RUN_ONCE_STATIC(ossl_init_config)
+{
+    int ret = openssl_config_int(NULL);
+
+    config_inited = 1;
+    return ret;
+}
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_config_settings, ossl_init_config)
 {
     int ret = openssl_config_int(conf_settings);
+
     config_inited = 1;
     return ret;
 }
@@ -456,6 +465,28 @@ void OPENSSL_cleanup(void)
  */
 int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
 {
+    uint64_t tmp;
+    int aloaddone = 0;
+
+    /*
+     * We ignore failures from this function. It is probably because we are
+     * on a platform that doesn't support lockless atomic loads (we may not
+     * have created init_lock yet so we can't use it). This is just an
+     * optimisation to skip the full checks in this function if we don't need
+     * to, so we carry on regardless in the event of failure.
+     *
+     * There could be a race here with other threads, so that optsdone has not
+     * been updated yet, even though the options have in fact been initialised.
+     * This doesn't matter - it just means we will run the full function
+     * unnecessarily - but all the critical code is contained in RUN_ONCE
+     * functions anyway so we are safe.
+     */
+    if (CRYPTO_atomic_load(&optsdone, &tmp, NULL)) {
+        if ((tmp & opts) == opts)
+            return 1;
+        aloaddone = 1;
+    }
+
     /*
      * TODO(3.0): This function needs looking at with a view to moving most/all
      * of this into OSSL_LIB_CTX.
@@ -463,7 +494,7 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
 
     if (stopped) {
         if (!(opts & OPENSSL_INIT_BASE_ONLY))
-            CRYPTOerr(CRYPTO_F_OPENSSL_INIT_CRYPTO, ERR_R_INIT_FAIL);
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INIT_FAIL);
         return 0;
     }
 
@@ -484,6 +515,18 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
     if (opts & OPENSSL_INIT_BASE_ONLY)
         return 1;
 
+    /*
+     * init_lock should definitely be set up now, so we can now repeat the
+     * same check from above but be sure that it will work even on platforms
+     * without lockless CRYPTO_atomic_load
+     */
+    if (!aloaddone) {
+        if (!CRYPTO_atomic_load(&optsdone, &tmp, init_lock))
+            return 0;
+        if ((tmp & opts) == opts)
+            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
@@ -539,11 +582,19 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
 
     if (opts & OPENSSL_INIT_LOAD_CONFIG) {
         int ret;
-        CRYPTO_THREAD_write_lock(init_lock);
-        conf_settings = settings;
-        ret = RUN_ONCE(&config, ossl_init_config);
-        conf_settings = NULL;
-        CRYPTO_THREAD_unlock(init_lock);
+
+        if (settings == NULL) {
+            ret = RUN_ONCE(&config, ossl_init_config);
+        } else {
+            if (!CRYPTO_THREAD_write_lock(init_lock))
+                return 0;
+            conf_settings = settings;
+            ret = RUN_ONCE_ALT(&config, ossl_init_config_settings,
+                               ossl_init_config);
+            conf_settings = NULL;
+            CRYPTO_THREAD_unlock(init_lock);
+        }
+
         if (ret <= 0)
             return 0;
     }
@@ -599,6 +650,9 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
         return 0;
 #endif
 
+    if (!CRYPTO_atomic_or(&optsdone, opts, &tmp, init_lock))
+        return 0;
+
     return 1;
 }
 
@@ -655,7 +709,7 @@ int OPENSSL_atexit(void (*handler)(void))
 #endif
 
     if ((newhand = OPENSSL_malloc(sizeof(*newhand))) == NULL) {
-        CRYPTOerr(CRYPTO_F_OPENSSL_ATEXIT, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
         return 0;
     }