diff --git a/crypto/provider_child.c b/crypto/provider_child.c index eb81c30a41..b1eadd5b19 100644 --- a/crypto/provider_child.c +++ b/crypto/provider_child.c @@ -269,26 +269,46 @@ void ossl_provider_deinit_child(OSSL_LIB_CTX *ctx) gbl->c_provider_deregister_child_cb(gbl->handle); } +/* + * ossl_provider_up_ref_parent() and ossl_provider_free_parent() do + * nothing in "self-referencing" child providers, i.e. when the parent + * of the child provider is the same as the provider where this child + * provider was created. + * This allows the teardown function in the parent provider to be called + * at the correct moment. + * For child providers in other providers, the reference count is done to + * ensure that cross referencing is recorded. These should be cleared up + * through that providers teardown, as part of freeing its child libctx. + */ + int ossl_provider_up_ref_parent(OSSL_PROVIDER *prov, int activate) { struct child_prov_globals *gbl; + const OSSL_CORE_HANDLE *parent_handle; gbl = ossl_lib_ctx_get_data(ossl_provider_libctx(prov), OSSL_LIB_CTX_CHILD_PROVIDER_INDEX); if (gbl == NULL) return 0; - return gbl->c_prov_up_ref(ossl_provider_get_parent(prov), activate); + parent_handle = ossl_provider_get_parent(prov); + if (parent_handle == gbl->handle) + return 1; + return gbl->c_prov_up_ref(parent_handle, activate); } int ossl_provider_free_parent(OSSL_PROVIDER *prov, int deactivate) { struct child_prov_globals *gbl; + const OSSL_CORE_HANDLE *parent_handle; gbl = ossl_lib_ctx_get_data(ossl_provider_libctx(prov), OSSL_LIB_CTX_CHILD_PROVIDER_INDEX); if (gbl == NULL) return 0; + parent_handle = ossl_provider_get_parent(prov); + if (parent_handle == gbl->handle) + return 1; return gbl->c_prov_free(ossl_provider_get_parent(prov), deactivate); }