From f11d0c7908b3124e2db72029e7e960294ca19b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bodo=20M=C3=B6ller?= Date: Fri, 5 Oct 2012 20:50:11 +0000 Subject: [PATCH] Fix EC_KEY initialization race. Submitted by: Adam Langley --- crypto/ec/ec.h | 10 +++++++++- crypto/ec/ec_key.c | 13 +++++++++++-- crypto/ecdh/ech_lib.c | 11 +++++++++-- crypto/ecdsa/ecs_lib.c | 11 +++++++++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/crypto/ec/ec.h b/crypto/ec/ec.h index ddb0e443b9..0dcad23294 100644 --- a/crypto/ec/ec.h +++ b/crypto/ec/ec.h @@ -810,7 +810,15 @@ void EC_KEY_set_conv_form(EC_KEY *eckey, point_conversion_form_t cform); /* functions to set/get method specific data */ void *EC_KEY_get_key_method_data(EC_KEY *key, void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *)); -void EC_KEY_insert_key_method_data(EC_KEY *key, void *data, +/** Sets the key method data of an EC_KEY object, if none has yet been set. + * \param key EC_KEY object + * \param data opaque data to install. + * \param dup_func a function that duplicates |data|. + * \param free_func a function that frees |data|. + * \param clear_free_func a function that wipes and frees |data|. + * \return the previously set data pointer, or NULL if |data| was inserted. + */ +void *EC_KEY_insert_key_method_data(EC_KEY *key, void *data, void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *)); /* wrapper functions for the underlying EC_GROUP object */ void EC_KEY_set_asn1_flag(EC_KEY *eckey, int asn1_flag); diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c index 24ae707560..a91b9dc7f4 100644 --- a/crypto/ec/ec_key.c +++ b/crypto/ec/ec_key.c @@ -604,18 +604,27 @@ void EC_KEY_set_conv_form(EC_KEY *key, point_conversion_form_t cform) void *EC_KEY_get_key_method_data(EC_KEY *key, void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *)) { - return EC_EX_DATA_get_data(key->method_data, dup_func, free_func, clear_free_func); + void *ret; + + CRYPTO_r_lock(CRYPTO_LOCK_EC); + ret = EC_EX_DATA_get_data(key->method_data, dup_func, free_func, clear_free_func); + CRYPTO_r_unlock(CRYPTO_LOCK_EC); + + return ret; } -void EC_KEY_insert_key_method_data(EC_KEY *key, void *data, +void *EC_KEY_insert_key_method_data(EC_KEY *key, void *data, void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *)) { EC_EXTRA_DATA *ex_data; + CRYPTO_w_lock(CRYPTO_LOCK_EC); ex_data = EC_EX_DATA_get_data(key->method_data, dup_func, free_func, clear_free_func); if (ex_data == NULL) EC_EX_DATA_set_data(&key->method_data, data, dup_func, free_func, clear_free_func); CRYPTO_w_unlock(CRYPTO_LOCK_EC); + + return ex_data; } void EC_KEY_set_asn1_flag(EC_KEY *key, int flag) diff --git a/crypto/ecdh/ech_lib.c b/crypto/ecdh/ech_lib.c index 4d8ea03d3d..75ff1da41d 100644 --- a/crypto/ecdh/ech_lib.c +++ b/crypto/ecdh/ech_lib.c @@ -210,8 +210,15 @@ ECDH_DATA *ecdh_check(EC_KEY *key) ecdh_data = (ECDH_DATA *)ecdh_data_new(); if (ecdh_data == NULL) return NULL; - EC_KEY_insert_key_method_data(key, (void *)ecdh_data, - ecdh_data_dup, ecdh_data_free, ecdh_data_free); + data = EC_KEY_insert_key_method_data(key, (void *)ecdh_data, + ecdh_data_dup, ecdh_data_free, ecdh_data_free); + if (data != NULL) + { + /* Another thread raced us to install the key_method + * data and won. */ + ecdh_data_free(ecdh_data); + ecdh_data = (ECDH_DATA *)data; + } } else ecdh_data = (ECDH_DATA *)data; diff --git a/crypto/ecdsa/ecs_lib.c b/crypto/ecdsa/ecs_lib.c index 2ebae3aa27..81082c9727 100644 --- a/crypto/ecdsa/ecs_lib.c +++ b/crypto/ecdsa/ecs_lib.c @@ -188,8 +188,15 @@ ECDSA_DATA *ecdsa_check(EC_KEY *key) ecdsa_data = (ECDSA_DATA *)ecdsa_data_new(); if (ecdsa_data == NULL) return NULL; - EC_KEY_insert_key_method_data(key, (void *)ecdsa_data, - ecdsa_data_dup, ecdsa_data_free, ecdsa_data_free); + data = EC_KEY_insert_key_method_data(key, (void *)ecdsa_data, + ecdsa_data_dup, ecdsa_data_free, ecdsa_data_free); + if (data != NULL) + { + /* Another thread raced us to install the key_method + * data and won. */ + ecdsa_data_free(ecdsa_data); + ecdsa_data = (ECDSA_DATA *)data; + } } else ecdsa_data = (ECDSA_DATA *)data; -- GitLab