Skip to content

Commit

Permalink
Merge pull request #4104 from gilles-peskine-arm/test-mutex-usage-cou…
Browse files Browse the repository at this point in the history
…nt-development

Test and fix mutex usage
  • Loading branch information
gilles-peskine-arm committed Feb 23, 2021
2 parents d6ee36e + b158321 commit a0fd0f8
Show file tree
Hide file tree
Showing 31 changed files with 649 additions and 76 deletions.
5 changes: 5 additions & 0 deletions ChangeLog.d/drbg-mutex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Bugfix
* Fix a resource leak in CTR_DRBG and HMAC_DRBG when MBEDTLS_THREADING_C
is enabled, on platforms where initializing a mutex allocates resources.
This was a regression introduced in the previous release. Reported in
#4017, #4045 and #4071.
13 changes: 13 additions & 0 deletions ChangeLog.d/rsa-mutex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Bugfix
* Ensure that calling mbedtls_rsa_free() or mbedtls_entropy_free()
twice is safe. This happens for RSA when some Mbed TLS library functions
fail. Such a double-free was not safe when MBEDTLS_THREADING_C was
enabled on platforms where freeing a mutex twice is not safe.
* Fix a resource leak in a bad-arguments case of mbedtls_rsa_gen_key()
when MBEDTLS_THREADING_C is enabled on platforms where initializing
a mutex allocates resources.

Default behavior changes
* In mbedtls_rsa_context objects, the ver field was formerly documented
as always 0. It is now reserved for internal purposes and may take
different values.
45 changes: 43 additions & 2 deletions include/mbedtls/ctr_drbg.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ typedef struct mbedtls_ctr_drbg_context
void *p_entropy; /*!< The context for the entropy function. */

#if defined(MBEDTLS_THREADING_C)
/* Invariant: the mutex is initialized if and only if f_entropy != NULL.
* This means that the mutex is initialized during the initial seeding
* in mbedtls_ctr_drbg_seed() and freed in mbedtls_ctr_drbg_free().
*
* Note that this invariant may change without notice. Do not rely on it
* and do not access the mutex directly in application code.
*/
mbedtls_threading_mutex_t mutex;
#endif
}
Expand Down Expand Up @@ -264,6 +271,15 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx );
* make a second call to \p f_entropy.
*/
#endif
#if defined(MBEDTLS_THREADING_C)
/**
* \note When Mbed TLS is built with threading support,
* after this function returns successfully,
* it is safe to call mbedtls_ctr_drbg_random()
* from multiple threads. Other operations, including
* reseeding, are not thread-safe.
*/
#endif /* MBEDTLS_THREADING_C */
/**
* - The \p custom string.
*
Expand All @@ -290,6 +306,8 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx );
* the same context unless you call
* mbedtls_ctr_drbg_free() and mbedtls_ctr_drbg_init()
* again first.
* After a failed call to mbedtls_ctr_drbg_seed(),
* you must call mbedtls_ctr_drbg_free().
* \param f_entropy The entropy callback, taking as arguments the
* \p p_entropy context, the buffer to fill, and the
* length of the buffer.
Expand Down Expand Up @@ -405,6 +423,11 @@ void mbedtls_ctr_drbg_set_reseed_interval( mbedtls_ctr_drbg_context *ctx,
* \brief This function reseeds the CTR_DRBG context, that is
* extracts data from the entropy source.
*
* \note This function is not thread-safe. It is not safe
* to call this function if another thread might be
* concurrently obtaining random numbers from the same
* context or updating or reseeding the same context.
*
* \param ctx The CTR_DRBG context.
* \param additional Additional data to add to the state. Can be \c NULL.
* \param len The length of the additional data.
Expand All @@ -422,6 +445,11 @@ int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx,
/**
* \brief This function updates the state of the CTR_DRBG context.
*
* \note This function is not thread-safe. It is not safe
* to call this function if another thread might be
* concurrently obtaining random numbers from the same
* context or updating or reseeding the same context.
*
* \param ctx The CTR_DRBG context.
* \param additional The data to update the state with. This must not be
* \c NULL unless \p add_len is \c 0.
Expand All @@ -445,6 +473,11 @@ int mbedtls_ctr_drbg_update_ret( mbedtls_ctr_drbg_context *ctx,
* This function automatically reseeds if the reseed counter is exceeded
* or prediction resistance is enabled.
*
* \note This function is not thread-safe. It is not safe
* to call this function if another thread might be
* concurrently obtaining random numbers from the same
* context or updating or reseeding the same context.
*
* \param p_rng The CTR_DRBG context. This must be a pointer to a
* #mbedtls_ctr_drbg_context structure.
* \param output The buffer to fill.
Expand Down Expand Up @@ -473,8 +506,16 @@ int mbedtls_ctr_drbg_random_with_add( void *p_rng,
*
* This function automatically reseeds if the reseed counter is exceeded
* or prediction resistance is enabled.
*
*
*/
#if defined(MBEDTLS_THREADING_C)
/**
* \note When Mbed TLS is built with threading support,
* it is safe to call mbedtls_ctr_drbg_random()
* from multiple threads. Other operations, including
* reseeding, are not thread-safe.
*/
#endif /* MBEDTLS_THREADING_C */
/**
* \param p_rng The CTR_DRBG context. This must be a pointer to a
* #mbedtls_ctr_drbg_context structure.
* \param output The buffer to fill.
Expand Down
6 changes: 4 additions & 2 deletions include/mbedtls/entropy.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,15 @@ mbedtls_entropy_source_state;
*/
typedef struct mbedtls_entropy_context
{
int accumulator_started;
int accumulator_started; /* 0 after init.
* 1 after the first update.
* -1 after free. */
#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR)
mbedtls_sha512_context accumulator;
#else
mbedtls_sha256_context accumulator;
#endif
int source_count;
int source_count; /* Number of entries used in source. */
mbedtls_entropy_source_state source[MBEDTLS_ENTROPY_MAX_SOURCES];
#if defined(MBEDTLS_HAVEGE_C)
mbedtls_havege_state havege_data;
Expand Down
58 changes: 55 additions & 3 deletions include/mbedtls/hmac_drbg.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ typedef struct mbedtls_hmac_drbg_context
void *p_entropy; /*!< context for the entropy function */

#if defined(MBEDTLS_THREADING_C)
/* Invariant: the mutex is initialized if and only if
* md_ctx->md_info != NULL. This means that the mutex is initialized
* during the initial seeding in mbedtls_hmac_drbg_seed() or
* mbedtls_hmac_drbg_seed_buf() and freed in mbedtls_ctr_drbg_free().
*
* Note that this invariant may change without notice. Do not rely on it
* and do not access the mutex directly in application code.
*/
mbedtls_threading_mutex_t mutex;
#endif
} mbedtls_hmac_drbg_context;
Expand Down Expand Up @@ -150,7 +158,17 @@ void mbedtls_hmac_drbg_init( mbedtls_hmac_drbg_context *ctx );
* \note During the initial seeding, this function calls
* the entropy source to obtain a nonce
* whose length is half the entropy length.
*
*/
#if defined(MBEDTLS_THREADING_C)
/**
* \note When Mbed TLS is built with threading support,
* after this function returns successfully,
* it is safe to call mbedtls_hmac_drbg_random()
* from multiple threads. Other operations, including
* reseeding, are not thread-safe.
*/
#endif /* MBEDTLS_THREADING_C */
/**
* \param ctx HMAC_DRBG context to be seeded.
* \param md_info MD algorithm to use for HMAC_DRBG.
* \param f_entropy The entropy callback, taking as arguments the
Expand Down Expand Up @@ -189,7 +207,17 @@ int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx,
*
* This function is meant for use in algorithms that need a pseudorandom
* input such as deterministic ECDSA.
*
*/
#if defined(MBEDTLS_THREADING_C)
/**
* \note When Mbed TLS is built with threading support,
* after this function returns successfully,
* it is safe to call mbedtls_hmac_drbg_random()
* from multiple threads. Other operations, including
* reseeding, are not thread-safe.
*/
#endif /* MBEDTLS_THREADING_C */
/**
* \param ctx HMAC_DRBG context to be initialised.
* \param md_info MD algorithm to use for HMAC_DRBG.
* \param data Concatenation of the initial entropy string and
Expand Down Expand Up @@ -252,6 +280,11 @@ void mbedtls_hmac_drbg_set_reseed_interval( mbedtls_hmac_drbg_context *ctx,
/**
* \brief This function updates the state of the HMAC_DRBG context.
*
* \note This function is not thread-safe. It is not safe
* to call this function if another thread might be
* concurrently obtaining random numbers from the same
* context or updating or reseeding the same context.
*
* \param ctx The HMAC_DRBG context.
* \param additional The data to update the state with.
* If this is \c NULL, there is no additional data.
Expand All @@ -268,6 +301,11 @@ int mbedtls_hmac_drbg_update_ret( mbedtls_hmac_drbg_context *ctx,
* \brief This function reseeds the HMAC_DRBG context, that is
* extracts data from the entropy source.
*
* \note This function is not thread-safe. It is not safe
* to call this function if another thread might be
* concurrently obtaining random numbers from the same
* context or updating or reseeding the same context.
*
* \param ctx The HMAC_DRBG context.
* \param additional Additional data to add to the state.
* If this is \c NULL, there is no additional data
Expand All @@ -293,6 +331,11 @@ int mbedtls_hmac_drbg_reseed( mbedtls_hmac_drbg_context *ctx,
* This function automatically reseeds if the reseed counter is exceeded
* or prediction resistance is enabled.
*
* \note This function is not thread-safe. It is not safe
* to call this function if another thread might be
* concurrently obtaining random numbers from the same
* context or updating or reseeding the same context.
*
* \param p_rng The HMAC_DRBG context. This must be a pointer to a
* #mbedtls_hmac_drbg_context structure.
* \param output The buffer to fill.
Expand Down Expand Up @@ -322,7 +365,16 @@ int mbedtls_hmac_drbg_random_with_add( void *p_rng,
*
* This function automatically reseeds if the reseed counter is exceeded
* or prediction resistance is enabled.
*
*/
#if defined(MBEDTLS_THREADING_C)
/**
* \note When Mbed TLS is built with threading support,
* it is safe to call mbedtls_ctr_drbg_random()
* from multiple threads. Other operations, including
* reseeding, are not thread-safe.
*/
#endif /* MBEDTLS_THREADING_C */
/**
* \param p_rng The HMAC_DRBG context. This must be a pointer to a
* #mbedtls_hmac_drbg_context structure.
* \param output The buffer to fill.
Expand Down
6 changes: 5 additions & 1 deletion include/mbedtls/rsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ extern "C" {
*/
typedef struct mbedtls_rsa_context
{
int ver; /*!< Always 0.*/
int ver; /*!< Reserved for internal purposes.
* Do not set this field in application
* code. Its meaning might change without
* notice. */
size_t len; /*!< The size of \p N in Bytes. */

mbedtls_mpi N; /*!< The public modulus. */
Expand Down Expand Up @@ -127,6 +130,7 @@ typedef struct mbedtls_rsa_context
mask generating function used in the
EME-OAEP and EMSA-PSS encodings. */
#if defined(MBEDTLS_THREADING_C)
/* Invariant: the mutex is initialized iff ver != 0. */
mbedtls_threading_mutex_t mutex; /*!< Thread-safety mutex. */
#endif
}
Expand Down
3 changes: 3 additions & 0 deletions include/mbedtls/threading.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ extern "C" {
typedef struct mbedtls_threading_mutex_t
{
pthread_mutex_t mutex;
/* is_valid is 0 after a failed init or a free, and nonzero after a
* successful init. This field is not considered part of the public
* API of Mbed TLS and may change without notice. */
char is_valid;
} mbedtls_threading_mutex_t;
#endif
Expand Down
16 changes: 8 additions & 8 deletions library/ctr_drbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx )
ctx->reseed_counter = -1;

ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL;

#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif
}

/*
Expand All @@ -72,15 +68,14 @@ void mbedtls_ctr_drbg_free( mbedtls_ctr_drbg_context *ctx )
return;

#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &ctx->mutex );
/* The mutex is initialized iff f_entropy is set. */
if( ctx->f_entropy != NULL )
mbedtls_mutex_free( &ctx->mutex );
#endif
mbedtls_aes_free( &ctx->aes_ctx );
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ctr_drbg_context ) );
ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL;
ctx->reseed_counter = -1;
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif
}

void mbedtls_ctr_drbg_set_prediction_resistance( mbedtls_ctr_drbg_context *ctx,
Expand Down Expand Up @@ -464,6 +459,11 @@ int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx,

memset( key, 0, MBEDTLS_CTR_DRBG_KEYSIZE );

/* The mutex is initialized iff f_entropy is set. */
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif

mbedtls_aes_init( &ctx->aes_ctx );

ctx->f_entropy = f_entropy;
Expand Down
7 changes: 6 additions & 1 deletion library/entropy.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ void mbedtls_entropy_init( mbedtls_entropy_context *ctx )

void mbedtls_entropy_free( mbedtls_entropy_context *ctx )
{
/* If the context was already free, don't call free() again.
* This is important for mutexes which don't allow double-free. */
if( ctx->accumulator_started == -1 )
return;

#if defined(MBEDTLS_HAVEGE_C)
mbedtls_havege_free( &ctx->havege_data );
#endif
Expand All @@ -132,7 +137,7 @@ void mbedtls_entropy_free( mbedtls_entropy_context *ctx )
#endif
ctx->source_count = 0;
mbedtls_platform_zeroize( ctx->source, sizeof( ctx->source ) );
ctx->accumulator_started = 0;
ctx->accumulator_started = -1;
}

int mbedtls_entropy_add_source( mbedtls_entropy_context *ctx,
Expand Down
20 changes: 12 additions & 8 deletions library/hmac_drbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ void mbedtls_hmac_drbg_init( mbedtls_hmac_drbg_context *ctx )
memset( ctx, 0, sizeof( mbedtls_hmac_drbg_context ) );

ctx->reseed_interval = MBEDTLS_HMAC_DRBG_RESEED_INTERVAL;

#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif
}

/*
Expand Down Expand Up @@ -129,6 +125,10 @@ int mbedtls_hmac_drbg_seed_buf( mbedtls_hmac_drbg_context *ctx,
if( ( ret = mbedtls_md_setup( &ctx->md_ctx, md_info, 1 ) ) != 0 )
return( ret );

#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif

/*
* Set initial working state.
* Use the V memory location, which is currently all 0, to initialize the
Expand Down Expand Up @@ -254,6 +254,11 @@ int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx,
if( ( ret = mbedtls_md_setup( &ctx->md_ctx, md_info, 1 ) ) != 0 )
return( ret );

/* The mutex is initialized iff the md context is set up. */
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif

md_size = mbedtls_md_get_size( md_info );

/*
Expand Down Expand Up @@ -421,14 +426,13 @@ void mbedtls_hmac_drbg_free( mbedtls_hmac_drbg_context *ctx )
return;

#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &ctx->mutex );
/* The mutex is initialized iff the md context is set up. */
if( ctx->md_ctx.md_info != NULL )
mbedtls_mutex_free( &ctx->mutex );
#endif
mbedtls_md_free( &ctx->md_ctx );
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_hmac_drbg_context ) );
ctx->reseed_interval = MBEDTLS_HMAC_DRBG_RESEED_INTERVAL;
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &ctx->mutex );
#endif
}

#if defined(MBEDTLS_FS_IO)
Expand Down
Loading

0 comments on commit a0fd0f8

Please sign in to comment.