From db6bcc81ab86fca74730566f0b471a7c3757c95c Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 22 Dec 2020 17:44:07 +0000 Subject: [PATCH] Optimise OPENSSL_init_crypto If everything has already been initialised we can check this with a single test at the beginning of OPENSSL_init_crypto() and therefore reduce the amount of time spent in this function. Since this is called via very many codepaths this should have significant performance benefits. Partially fixes #13725 and #13578 Reviewed-by: Dmitry Belyavskiy (Merged from https://github.com/openssl/openssl/pull/13733) --- crypto/init.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crypto/init.c b/crypto/init.c index f1100df169b..50aec32c3d1 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -34,6 +34,7 @@ #include static int stopped = 0; +static uint64_t optsdone = 0; typedef struct ossl_init_stop_st OPENSSL_INIT_STOP; struct ossl_init_stop_st { @@ -464,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. @@ -492,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 @@ -614,6 +649,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; } -- 2.47.2