summaryrefslogtreecommitdiffstats
path: root/crypto/provider_core.c
diff options
context:
space:
mode:
authorRichard Levitte <levitte@openssl.org>2020-12-16 15:15:06 +0100
committerRichard Levitte <levitte@openssl.org>2020-12-17 12:02:08 +0100
commit390f9bad69ce19f601abf131ceabf90aedc0d3d5 (patch)
tree33336c07e8a35b8aa45dc7b6d9df6705d6a94643 /crypto/provider_core.c
parent6963979f5c0f95b2152ef74645faa7344e33284d (diff)
CORE: Separate OSSL_PROVIDER activation from OSSL_PROVIDER reference
This introduces a separate activation counter, and the function ossl_provider_deactivate() for provider deactivation. Something to be noted is that if the reference count goes down to zero, we don't care if the activation count is non-zero (i.e. someone forgot to call ossl_provider_deactivate()). Since there are no more references to the provider, it doesn't matter. The important thing is that deactivation doesn't remove the provider as long as there are references to it, for example because there are live methods associated with that provider, but still makes the provider unavailable to create new methods from. Fixes #13503 Fixes #12157 Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org> (Merged from https://github.com/openssl/openssl/pull/13661)
Diffstat (limited to 'crypto/provider_core.c')
-rw-r--r--crypto/provider_core.c130
1 files changed, 80 insertions, 50 deletions
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index 954befd4a2..f0d6fb20f8 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -44,12 +44,15 @@ struct provider_store_st; /* Forward declaration */
struct ossl_provider_st {
/* Flag bits */
unsigned int flag_initialized:1;
+ unsigned int flag_activated:1;
unsigned int flag_fallback:1; /* Can be used as fallback */
unsigned int flag_activated_as_fallback:1;
/* OpenSSL library side data */
CRYPTO_REF_COUNT refcnt;
CRYPTO_RWLOCK *refcnt_lock; /* For the ref counter */
+ CRYPTO_REF_COUNT activatecnt;
+ CRYPTO_RWLOCK *activatecnt_lock; /* For the activate counter */
char *name;
char *path;
DSO *module;
@@ -110,20 +113,15 @@ struct provider_store_st {
};
/*
- * provider_deactivate_free() is a wrapper around ossl_provider_free()
- * that also makes sure that activated fallback providers are deactivated.
- * This is simply done by freeing them an extra time, to compensate for the
- * refcount that provider_activate_fallbacks() gives them.
+ * provider_deactivate_free() is a wrapper around ossl_provider_deactivate()
+ * and ossl_provider_free(), called as needed.
* Since this is only called when the provider store is being emptied, we
* don't need to care about any lock.
*/
static void provider_deactivate_free(OSSL_PROVIDER *prov)
{
- int extra_free = (prov->flag_initialized
- && prov->flag_activated_as_fallback);
-
- if (extra_free)
- ossl_provider_free(prov);
+ if (prov->flag_activated)
+ ossl_provider_deactivate(prov);
ossl_provider_free(prov);
}
@@ -251,6 +249,7 @@ static OSSL_PROVIDER *provider_new(const char *name,
if ((prov = OPENSSL_zalloc(sizeof(*prov))) == NULL
#ifndef HAVE_ATOMICS
|| (prov->refcnt_lock = CRYPTO_THREAD_lock_new()) == NULL
+ || (prov->activatecnt_lock = CRYPTO_THREAD_lock_new()) == NULL
#endif
|| !ossl_provider_up_ref(prov) /* +1 One reference to be returned */
|| (prov->name = OPENSSL_strdup(name)) == NULL) {
@@ -337,38 +336,35 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
CRYPTO_DOWN_REF(&prov->refcnt, &ref, prov->refcnt_lock);
/*
- * When the refcount drops below two, the store is the only
- * possible reference, or it has already been taken away from
- * the store (this may happen if a provider was activated
- * because it's a fallback, but isn't currently used)
- * When that happens, the provider is inactivated.
+ * When the refcount drops to zero, we clean up the provider.
+ * Note that this also does teardown, which may seem late,
+ * considering that init happens on first activation. However,
+ * there may be other structures hanging on to the provider after
+ * the last deactivation and may therefore need full access to the
+ * provider's services. Therefore, we deinit late.
*/
- if (ref < 2 && prov->flag_initialized) {
+ if (ref == 0) {
+ if (prov->flag_initialized) {
#ifndef FIPS_MODULE
- ossl_init_thread_deregister(prov);
+ ossl_init_thread_deregister(prov);
#endif
- if (prov->teardown != NULL)
- prov->teardown(prov->provctx);
+ if (prov->teardown != NULL)
+ prov->teardown(prov->provctx);
#ifndef OPENSSL_NO_ERR
# ifndef FIPS_MODULE
- if (prov->error_strings != NULL) {
- ERR_unload_strings(prov->error_lib, prov->error_strings);
- OPENSSL_free(prov->error_strings);
- prov->error_strings = NULL;
- }
+ if (prov->error_strings != NULL) {
+ ERR_unload_strings(prov->error_lib, prov->error_strings);
+ OPENSSL_free(prov->error_strings);
+ prov->error_strings = NULL;
+ }
# endif
#endif
- OPENSSL_free(prov->operation_bits);
- prov->operation_bits = NULL;
- prov->operation_bits_sz = 0;
- prov->flag_initialized = 0;
- }
+ OPENSSL_free(prov->operation_bits);
+ prov->operation_bits = NULL;
+ prov->operation_bits_sz = 0;
+ prov->flag_initialized = 0;
+ }
- /*
- * When the refcount drops to zero, it has been taken out of
- * the store. All we have to do here is clean it out.
- */
- if (ref == 0) {
#ifndef FIPS_MODULE
DSO_free(prov->module);
#endif
@@ -377,6 +373,7 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
sk_INFOPAIR_pop_free(prov->parameters, free_infopair);
#ifndef HAVE_ATOMICS
CRYPTO_THREAD_lock_free(prov->refcnt_lock);
+ CRYPTO_THREAD_lock_free(prov->activatecnt_lock);
#endif
OPENSSL_free(prov);
}
@@ -460,7 +457,7 @@ int OSSL_PROVIDER_set_default_search_path(OSSL_LIB_CTX *libctx,
* locking. Direct callers must remember to set the store flags when
* appropriate.
*/
-static int provider_activate(OSSL_PROVIDER *prov)
+static int provider_init(OSSL_PROVIDER *prov)
{
const OSSL_DISPATCH *provider_dispatch = NULL;
void *tmp_provctx = NULL; /* safety measure */
@@ -633,18 +630,60 @@ static int provider_activate(OSSL_PROVIDER *prov)
return 1;
}
+static int provider_deactivate(OSSL_PROVIDER *prov)
+{
+ int ref = 0;
+
+ if (!ossl_assert(prov != NULL))
+ return 0;
+
+ if (CRYPTO_DOWN_REF(&prov->activatecnt, &ref, prov->activatecnt_lock) <= 0)
+ return 0;
+
+ if (ref < 1)
+ prov->flag_activated = 0;
+
+ /* We don't deinit here, that's done in ossl_provider_free() */
+ return 1;
+}
+
+static int provider_activate(OSSL_PROVIDER *prov)
+{
+ int ref = 0;
+
+ if (CRYPTO_UP_REF(&prov->activatecnt, &ref, prov->activatecnt_lock) <= 0)
+ return 0;
+
+ if (provider_init(prov)) {
+ prov->flag_activated = 1;
+
+ return 1;
+ }
+
+ provider_deactivate(prov);
+ return 0;
+}
+
int ossl_provider_activate(OSSL_PROVIDER *prov)
{
+ if (prov == NULL)
+ return 0;
if (provider_activate(prov)) {
CRYPTO_THREAD_write_lock(prov->store->lock);
prov->store->use_fallbacks = 0;
CRYPTO_THREAD_unlock(prov->store->lock);
return 1;
}
-
return 0;
}
+int ossl_provider_deactivate(OSSL_PROVIDER *prov)
+{
+ if (prov == NULL)
+ return 0;
+ return provider_deactivate(prov);
+}
+
void *ossl_provider_ctx(const OSSL_PROVIDER *prov)
{
return prov->provctx;
@@ -669,7 +708,7 @@ static int provider_forall_loaded(struct provider_store_st *store,
OSSL_PROVIDER *prov =
sk_OSSL_PROVIDER_value(store->providers, i);
- if (prov->flag_initialized) {
+ if (prov->flag_activated) {
if (found_activated != NULL)
*found_activated = 1;
if (!(ret = cb(prov, cbdata)))
@@ -695,23 +734,14 @@ static void provider_activate_fallbacks(struct provider_store_st *store)
for (i = 0; i < num_provs; i++) {
OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i);
- /*
- * Activated fallback providers get an extra refcount, to
- * simulate a regular load.
- * Note that we don't care if the activation succeeds or not,
- * other than to maintain a correct refcount. If the activation
- * doesn't succeed, then any future attempt to use the fallback
- * provider will fail anyway.
- */
- if (prov->flag_fallback) {
- if (ossl_provider_up_ref(prov)) {
- if (!provider_activate(prov)) {
- ossl_provider_free(prov);
- } else {
+ if (ossl_provider_up_ref(prov)) {
+ if (prov->flag_fallback) {
+ if (provider_activate(prov)) {
prov->flag_activated_as_fallback = 1;
activated_fallback_count++;
}
}
+ ossl_provider_free(prov);
}
}
@@ -765,7 +795,7 @@ int ossl_provider_available(OSSL_PROVIDER *prov)
provider_activate_fallbacks(prov->store);
CRYPTO_THREAD_unlock(prov->store->lock);
- return prov->flag_initialized;
+ return prov->flag_activated;
}
return 0;
}