-
-
Notifications
You must be signed in to change notification settings - Fork 9.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't get a lock when querying the parent reseed_count (alternative version) #20970
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,21 +45,20 @@ static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch, | |
|
||
static int rand_drbg_restart(PROV_DRBG *drbg); | ||
|
||
/* | ||
* We interpret a call to this function as a hint only and ignore it. This | ||
* occurs when the EVP layer thinks we should do some locking. In practice | ||
* however we manage for ourselves when we take a lock or not on the basis | ||
* of whether drbg->lock is present or not. | ||
*/ | ||
int ossl_drbg_lock(void *vctx) | ||
{ | ||
PROV_DRBG *drbg = vctx; | ||
|
||
if (drbg == NULL || drbg->lock == NULL) | ||
return 1; | ||
return CRYPTO_THREAD_write_lock(drbg->lock); | ||
return 1; | ||
} | ||
|
||
/* Interpreted as a hint only and ignored as for ossl_drbg_lock() */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: In a similar vein, a counter/metric to track this? Even if just in a debug build? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above |
||
void ossl_drbg_unlock(void *vctx) | ||
{ | ||
PROV_DRBG *drbg = vctx; | ||
|
||
if (drbg != NULL && drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
} | ||
|
||
static int ossl_drbg_lock_parent(PROV_DRBG *drbg) | ||
|
@@ -484,16 +483,12 @@ int ossl_prov_drbg_uninstantiate(PROV_DRBG *drbg) | |
return 1; | ||
} | ||
|
||
/* | ||
* Reseed |drbg|, mixing in the specified data | ||
* | ||
* Requires that drbg->lock is already locked for write, if non-null. | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
*/ | ||
int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance, | ||
const unsigned char *ent, size_t ent_len, | ||
const unsigned char *adin, size_t adinlen) | ||
static int ossl_prov_drbg_reseed_unlocked(PROV_DRBG *drbg, | ||
int prediction_resistance, | ||
const unsigned char *ent, | ||
size_t ent_len, | ||
const unsigned char *adin, | ||
size_t adinlen) | ||
{ | ||
unsigned char *entropy = NULL; | ||
size_t entropylen = 0; | ||
|
@@ -595,12 +590,37 @@ int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance, | |
return 0; | ||
} | ||
|
||
/* | ||
* Reseed |drbg|, mixing in the specified data | ||
* | ||
* Acquires the drbg->lock for writing, if non-null. | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
*/ | ||
int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance, | ||
const unsigned char *ent, size_t ent_len, | ||
const unsigned char *adin, size_t adinlen) | ||
{ | ||
int ret; | ||
|
||
if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) | ||
return 0; | ||
|
||
ret = ossl_prov_drbg_reseed_unlocked(drbg, prediction_resistance, ent, | ||
ent_len, adin, adinlen); | ||
|
||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
|
||
return ret; | ||
} | ||
|
||
/* | ||
* Generate |outlen| bytes into the buffer at |out|. Reseed if we need | ||
* to or if |prediction_resistance| is set. Additional input can be | ||
* sent in |adin| and |adinlen|. | ||
* | ||
* Requires that drbg->lock is already locked for write, if non-null. | ||
* Acquires the drbg->lock for writing if available | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
* | ||
|
@@ -611,35 +631,39 @@ int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen, | |
{ | ||
int fork_id; | ||
int reseed_required = 0; | ||
int ret = 0; | ||
|
||
if (!ossl_prov_is_running()) | ||
return 0; | ||
|
||
if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) | ||
return 0; | ||
|
||
if (drbg->state != EVP_RAND_STATE_READY) { | ||
/* try to recover from previous errors */ | ||
rand_drbg_restart(drbg); | ||
|
||
if (drbg->state == EVP_RAND_STATE_ERROR) { | ||
ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE); | ||
return 0; | ||
goto err; | ||
} | ||
if (drbg->state == EVP_RAND_STATE_UNINITIALISED) { | ||
ERR_raise(ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED); | ||
return 0; | ||
goto err; | ||
} | ||
} | ||
if (strength > drbg->strength) { | ||
ERR_raise(ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH); | ||
return 0; | ||
goto err; | ||
} | ||
|
||
if (outlen > drbg->max_request) { | ||
ERR_raise(ERR_LIB_PROV, PROV_R_REQUEST_TOO_LARGE_FOR_DRBG); | ||
return 0; | ||
goto err; | ||
} | ||
if (adinlen > drbg->max_adinlen) { | ||
ERR_raise(ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG); | ||
return 0; | ||
goto err; | ||
} | ||
|
||
fork_id = openssl_get_fork_id(); | ||
|
@@ -664,10 +688,10 @@ int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen, | |
reseed_required = 1; | ||
|
||
if (reseed_required || prediction_resistance) { | ||
if (!ossl_prov_drbg_reseed(drbg, prediction_resistance, NULL, 0, | ||
adin, adinlen)) { | ||
if (!ossl_prov_drbg_reseed_unlocked(drbg, prediction_resistance, NULL, | ||
0, adin, adinlen)) { | ||
ERR_raise(ERR_LIB_PROV, PROV_R_RESEED_ERROR); | ||
return 0; | ||
goto err; | ||
} | ||
adin = NULL; | ||
adinlen = 0; | ||
|
@@ -676,12 +700,17 @@ int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen, | |
if (!drbg->generate(drbg, out, outlen, adin, adinlen)) { | ||
drbg->state = EVP_RAND_STATE_ERROR; | ||
ERR_raise(ERR_LIB_PROV, PROV_R_GENERATE_ERROR); | ||
return 0; | ||
goto err; | ||
} | ||
|
||
drbg->generate_counter++; | ||
|
||
return 1; | ||
ret = 1; | ||
err: | ||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
|
||
return ret; | ||
} | ||
|
||
/* | ||
|
@@ -848,6 +877,10 @@ void ossl_rand_drbg_free(PROV_DRBG *drbg) | |
OPENSSL_free(drbg); | ||
} | ||
|
||
/* | ||
* Helper function called by internal DRBG implementations. Assumes that at | ||
* least a read lock has been taken on drbg->lock | ||
*/ | ||
int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[]) | ||
{ | ||
OSSL_PARAM *p; | ||
|
@@ -860,10 +893,6 @@ int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[]) | |
if (p != NULL && !OSSL_PARAM_set_int(p, drbg->strength)) | ||
return 0; | ||
|
||
p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); | ||
if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_request)) | ||
return 0; | ||
|
||
p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MIN_ENTROPYLEN); | ||
if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->min_entropylen)) | ||
return 0; | ||
|
@@ -900,10 +929,43 @@ int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[]) | |
if (p != NULL && !OSSL_PARAM_set_time_t(p, drbg->reseed_time_interval)) | ||
return 0; | ||
|
||
return 1; | ||
} | ||
|
||
/* | ||
* Helper function to get certain params that require no lock to obtain. Sets | ||
* *complete to 1 if all the params were processed, or 0 otherwise | ||
*/ | ||
int ossl_drbg_get_ctx_params_no_lock(PROV_DRBG *drbg, OSSL_PARAM params[], | ||
int *complete) | ||
{ | ||
size_t cnt = 0; | ||
OSSL_PARAM *p; | ||
|
||
/* This value never changes once set */ | ||
p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); | ||
if (p != NULL) { | ||
if (!OSSL_PARAM_set_size_t(p, drbg->max_request)) | ||
return 0; | ||
cnt++; | ||
} | ||
|
||
/* | ||
* Can be changed by multiple threads, but we tolerate inaccuracies in this | ||
* value. | ||
*/ | ||
p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_COUNTER); | ||
if (p != NULL | ||
&& !OSSL_PARAM_set_uint(p, tsan_load(&drbg->reseed_counter))) | ||
return 0; | ||
if (p != NULL) { | ||
if (!OSSL_PARAM_set_uint(p, tsan_load(&drbg->reseed_counter))) | ||
return 0; | ||
cnt++; | ||
} | ||
|
||
if (params[cnt].key == NULL) | ||
*complete = 1; | ||
else | ||
*complete = 0; | ||
|
||
return 1; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,8 @@ static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_ctr_gettable_ctx_params; | |
static OSSL_FUNC_rand_get_ctx_params_fn drbg_ctr_get_ctx_params; | ||
static OSSL_FUNC_rand_verify_zeroization_fn drbg_ctr_verify_zeroization; | ||
|
||
static int drbg_ctr_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]); | ||
|
||
/* | ||
* The state of a DRBG AES-CTR. | ||
*/ | ||
|
@@ -330,11 +332,20 @@ static int drbg_ctr_instantiate_wrapper(void *vdrbg, unsigned int strength, | |
const OSSL_PARAM params[]) | ||
{ | ||
PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; | ||
int ret = 0; | ||
|
||
if (!ossl_prov_is_running() || !drbg_ctr_set_ctx_params(drbg, params)) | ||
if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) | ||
return 0; | ||
return ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance, | ||
pstr, pstr_len); | ||
|
||
if (!ossl_prov_is_running() | ||
|| !drbg_ctr_set_ctx_params_locked(drbg, params)) | ||
goto err; | ||
ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance, | ||
pstr, pstr_len); | ||
err: | ||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
return ret; | ||
} | ||
|
||
static int drbg_ctr_reseed(PROV_DRBG *drbg, | ||
|
@@ -473,21 +484,41 @@ static int drbg_ctr_uninstantiate(PROV_DRBG *drbg) | |
|
||
static int drbg_ctr_uninstantiate_wrapper(void *vdrbg) | ||
{ | ||
return drbg_ctr_uninstantiate((PROV_DRBG *)vdrbg); | ||
PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; | ||
int ret; | ||
|
||
if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it worth moving the lock/unlock code into drbg.c? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure it's worth it. It's like one if line for lock and similarly for unlock. |
||
return 0; | ||
|
||
ret = drbg_ctr_uninstantiate(drbg); | ||
|
||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
|
||
return ret; | ||
} | ||
|
||
static int drbg_ctr_verify_zeroization(void *vdrbg) | ||
{ | ||
PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; | ||
PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; | ||
int ret = 0; | ||
|
||
if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) | ||
return 0; | ||
|
||
PROV_DRBG_VERYIFY_ZEROIZATION(ctr->K); | ||
PROV_DRBG_VERYIFY_ZEROIZATION(ctr->V); | ||
PROV_DRBG_VERYIFY_ZEROIZATION(ctr->bltmp); | ||
PROV_DRBG_VERYIFY_ZEROIZATION(ctr->KX); | ||
if (ctr->bltmp_pos != 0) | ||
return 0; | ||
return 1; | ||
goto err; | ||
|
||
ret = 1; | ||
err: | ||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
return ret; | ||
} | ||
|
||
static int drbg_ctr_init_lengths(PROV_DRBG *drbg) | ||
|
@@ -627,20 +658,35 @@ static int drbg_ctr_get_ctx_params(void *vdrbg, OSSL_PARAM params[]) | |
PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; | ||
PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; | ||
OSSL_PARAM *p; | ||
int ret = 0, complete = 0; | ||
|
||
if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete)) | ||
return 0; | ||
|
||
if (complete) | ||
return 1; | ||
|
||
if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) | ||
return 0; | ||
|
||
p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_USE_DF); | ||
if (p != NULL && !OSSL_PARAM_set_int(p, ctr->use_df)) | ||
return 0; | ||
goto err; | ||
|
||
p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_CIPHER); | ||
if (p != NULL) { | ||
if (ctr->cipher_ctr == NULL | ||
|| !OSSL_PARAM_set_utf8_string(p, | ||
EVP_CIPHER_get0_name(ctr->cipher_ctr))) | ||
return 0; | ||
goto err; | ||
} | ||
|
||
return ossl_drbg_get_ctx_params(drbg, params); | ||
ret = ossl_drbg_get_ctx_params(drbg, params); | ||
err: | ||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
|
||
return ret; | ||
} | ||
|
||
static const OSSL_PARAM *drbg_ctr_gettable_ctx_params(ossl_unused void *vctx, | ||
|
@@ -655,7 +701,7 @@ static const OSSL_PARAM *drbg_ctr_gettable_ctx_params(ossl_unused void *vctx, | |
return known_gettable_ctx_params; | ||
} | ||
|
||
static int drbg_ctr_set_ctx_params(void *vctx, const OSSL_PARAM params[]) | ||
static int drbg_ctr_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]) | ||
{ | ||
PROV_DRBG *ctx = (PROV_DRBG *)vctx; | ||
PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)ctx->data; | ||
|
@@ -712,6 +758,22 @@ static int drbg_ctr_set_ctx_params(void *vctx, const OSSL_PARAM params[]) | |
return ossl_drbg_set_ctx_params(ctx, params); | ||
} | ||
|
||
static int drbg_ctr_set_ctx_params(void *vctx, const OSSL_PARAM params[]) | ||
{ | ||
PROV_DRBG *drbg = (PROV_DRBG *)vctx; | ||
int ret; | ||
|
||
if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) | ||
return 0; | ||
|
||
ret = drbg_ctr_set_ctx_params_locked(vctx, params); | ||
|
||
if (drbg->lock != NULL) | ||
CRYPTO_THREAD_unlock(drbg->lock); | ||
|
||
return ret; | ||
} | ||
|
||
static const OSSL_PARAM *drbg_ctr_settable_ctx_params(ossl_unused void *vctx, | ||
ossl_unused void *provctx) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just wrong: the primary DRBG will no longer be protected by a lock and it is accessed by multiple threads. Chaos ensues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the case. The primary DRBG is still locked. It just does not lock in response to the evp lock/unlock calls any more. The DRBG manages its own locking instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Should this be incrementing a counter/metric or something, so we can "get a hint" if something needs to happen here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For what purpose? It's slightly tricky to do. It would probably have to be implemented as an atomic to give reliable results. But I'm not sure what we then do with that information? It can't really be used for any decision making. An increment of the counter just tells you that a thread somewhere thinks a lock is necessary for the thing it wants to do. You could validate that the number of lock calls equals the number of unlock calls....but you can only really do that when the drbg is finished with. I suppose you could detect that we've had more unlock calls that lock calls immediately. But these are all "should not happen" scenarios which could only really be used for "assert" checking. I'm not sure the extra cost of an atomic is worth it for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, then, perhaps it should just be removed? If it's not actually be used to "hint at" anything, it's kinda pointless to keep it. Doesn't block my approval, though.