Merge pull request #4104 from gilles-peskine-arm/test-mutex-usage-count-development

Test and fix mutex usage
This commit is contained in:
Gilles Peskine 2021-02-23 15:14:43 +01:00 committed by GitHub
commit a0fd0f8531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 649 additions and 76 deletions

View File

@ -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
ChangeLog.d/rsa-mutex.txt Normal file
View File

@ -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.

View File

@ -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
}
@ -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.
*
@ -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.
@ -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.
@ -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.
@ -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.
@ -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.

View File

@ -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;

View File

@ -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;
@ -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
@ -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
@ -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.
@ -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
@ -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.
@ -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.

View File

@ -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. */
@ -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
}

View File

@ -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

View File

@ -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
}
/*
@ -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,
@ -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;

View File

@ -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
@ -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,

View File

@ -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
}
/*
@ -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
@ -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 );
/*
@ -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)

View File

@ -490,6 +490,9 @@ void mbedtls_rsa_init( mbedtls_rsa_context *ctx,
mbedtls_rsa_set_padding( ctx, padding, hash_id );
#if defined(MBEDTLS_THREADING_C)
/* Set ctx->ver to nonzero to indicate that the mutex has been
* initialized and will need to be freed. */
ctx->ver = 1;
mbedtls_mutex_init( &ctx->mutex );
#endif
}
@ -537,9 +540,6 @@ int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
RSA_VALIDATE_RET( ctx != NULL );
RSA_VALIDATE_RET( f_rng != NULL );
if( nbits < 128 || exponent < 3 || nbits % 2 != 0 )
return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
/*
* If the modulus is 1024 bit long or shorter, then the security strength of
* the RSA algorithm is less than or equal to 80 bits and therefore an error
@ -552,6 +552,12 @@ int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx,
mbedtls_mpi_init( &G );
mbedtls_mpi_init( &L );
if( nbits < 128 || exponent < 3 || nbits % 2 != 0 )
{
ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA;
goto cleanup;
}
/*
* find primes P and Q with Q < P so that:
* 1. |P-Q| > 2^( nbits / 2 - 100 )
@ -629,7 +635,9 @@ cleanup:
if( ret != 0 )
{
mbedtls_rsa_free( ctx );
return( MBEDTLS_ERR_RSA_KEY_GEN_FAILED + ret );
if( ( -ret & ~0x7f ) == 0 )
ret = MBEDTLS_ERR_RSA_KEY_GEN_FAILED + ret;
return( ret );
}
return( 0 );
@ -2481,7 +2489,6 @@ int mbedtls_rsa_copy( mbedtls_rsa_context *dst, const mbedtls_rsa_context *src )
RSA_VALIDATE_RET( dst != NULL );
RSA_VALIDATE_RET( src != NULL );
dst->ver = src->ver;
dst->len = src->len;
MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &dst->N, &src->N ) );
@ -2540,7 +2547,12 @@ void mbedtls_rsa_free( mbedtls_rsa_context *ctx )
#endif /* MBEDTLS_RSA_NO_CRT */
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &ctx->mutex );
/* Free the mutex, but only if it hasn't been freed already. */
if( ctx->ver != 0 )
{
mbedtls_mutex_free( &ctx->mutex );
ctx->ver = 0;
}
#endif
}

View File

@ -67,6 +67,12 @@ static void threading_mutex_init_pthread( mbedtls_threading_mutex_t *mutex )
if( mutex == NULL )
return;
/* A nonzero value of is_valid indicates a successfully initialized
* mutex. This is a workaround for not being able to return an error
* code for this function. The lock/unlock functions return an error
* if is_valid is nonzero. The Mbed TLS unit test code uses this field
* to distinguish more states of the mutex; see
* tests/src/threading_helpers for details. */
mutex->is_valid = pthread_mutex_init( &mutex->mutex, NULL ) == 0;
}

View File

@ -734,6 +734,10 @@ int main( int argc, char *argv[] )
mbedtls_memory_buffer_alloc_init( alloc_buf, sizeof(alloc_buf) );
#endif
#if defined(MBEDTLS_TEST_HOOKS)
test_hooks_init( );
#endif /* MBEDTLS_TEST_HOOKS */
/*
* Make sure memory references are valid.
*/
@ -3036,6 +3040,20 @@ exit:
mbedtls_free( context_buf );
#endif
#if defined(MBEDTLS_USE_PSA_CRYPTO)
mbedtls_psa_crypto_free( );
#endif
#if defined(MBEDTLS_TEST_HOOKS)
if( test_hooks_failure_detected( ) )
{
if( ret == 0 )
ret = 1;
mbedtls_printf( "Test hooks detected errors.\n" );
}
test_hooks_free( );
#endif /* MBEDTLS_TEST_HOOKS */
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_memory_buffer_alloc_status();

View File

@ -1369,6 +1369,10 @@ int main( int argc, char *argv[] )
#endif /* MBEDTLS_MEMORY_DEBUG */
#endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */
#if defined(MBEDTLS_TEST_HOOKS)
test_hooks_init( );
#endif /* MBEDTLS_TEST_HOOKS */
/*
* Make sure memory references are valid in case we exit early.
*/
@ -3998,6 +4002,26 @@ exit:
mbedtls_free( context_buf );
#endif
#if defined(MBEDTLS_USE_PSA_CRYPTO)
mbedtls_psa_crypto_free( );
#endif
#if defined(MBEDTLS_TEST_HOOKS)
/* Let test hooks detect errors such as resource leaks.
* Don't do it in query_config mode, because some test code prints
* information to stdout and this gets mixed with the regular output. */
if( opt.query_config_mode == DFL_QUERY_CONFIG_MODE )
{
if( test_hooks_failure_detected( ) )
{
if( ret == 0 )
ret = 1;
mbedtls_printf( "Test hooks detected errors.\n" );
}
}
test_hooks_free( );
#endif /* MBEDTLS_TEST_HOOKS */
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_memory_buffer_alloc_status();

View File

@ -22,6 +22,10 @@
#include "ssl_test_lib.h"
#if defined(MBEDTLS_TEST_HOOKS)
#include "test/helpers.h"
#endif
#if !defined(MBEDTLS_SSL_TEST_IMPOSSIBLE)
void my_debug( void *ctx, int level,
@ -321,4 +325,33 @@ int idle( mbedtls_net_context *fd,
return( 0 );
}
#if defined(MBEDTLS_TEST_HOOKS)
void test_hooks_init( void )
{
mbedtls_test_info_reset( );
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
mbedtls_test_mutex_usage_init( );
#endif
}
int test_hooks_failure_detected( void )
{
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
/* Errors are reported via mbedtls_test_info. */
mbedtls_test_mutex_usage_check( );
#endif
if( mbedtls_test_info.result != MBEDTLS_TEST_RESULT_SUCCESS )
return( 1 );
return( 0 );
}
void test_hooks_free( void )
{
}
#endif /* MBEDTLS_TEST_HOOKS */
#endif /* !defined(MBEDTLS_SSL_TEST_IMPOSSIBLE) */

View File

@ -258,5 +258,37 @@ int idle( mbedtls_net_context *fd,
#endif
int idle_reason );
#if defined(MBEDTLS_TEST_HOOKS)
/** Initialize whatever test hooks are enabled by the compile-time
* configuration and make sense for the TLS test programs. */
void test_hooks_init( void );
/** Check if any test hooks detected a problem.
*
* If a problem was detected, it's ok for the calling program to keep going,
* but it should ultimately exit with an error status.
*
* \note When implementing a test hook that detects errors on its own
* (as opposed to e.g. leaving the error for a memory sanitizer to
* report), make sure to print a message to standard error either at
* the time the problem is detected or during the execution of this
* function. This function does not indicate what problem was detected,
* so printing a message is the only way to provide feedback in the
* logs of the calling program.
*
* \return Nonzero if a problem was detected.
* \c 0 if no problem was detected.
*/
int test_hooks_failure_detected( void );
/** Free any resources allocated for the sake of test hooks.
*
* Call this at the end of the program so that resource leak analyzers
* don't complain.
*/
void test_hooks_free( void );
#endif /* !MBEDTLS_TEST_HOOKS */
#endif /* MBEDTLS_SSL_TEST_IMPOSSIBLE conditions: else */
#endif /* MBEDTLS_PROGRAMS_SSL_SSL_TEST_LIB_H */

View File

@ -31,6 +31,11 @@
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_THREADING_C) && defined(MBEDTLS_THREADING_PTHREAD) && \
defined(MBEDTLS_TEST_HOOKS)
#define MBEDTLS_TEST_MUTEX_USAGE
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
@ -63,6 +68,9 @@ typedef struct
const char *filename;
int line_no;
unsigned long step;
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
const char *mutex_usage_error;
#endif
}
mbedtls_test_info_t;
extern mbedtls_test_info_t mbedtls_test_info;
@ -260,4 +268,14 @@ void mbedtls_test_param_failed_reset_state( void );
#include "test/fake_external_rng_for_test.h"
#endif
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
/** Permanently activate the mutex usage verification framework. See
* threading_helpers.c for information. */
void mbedtls_test_mutex_usage_init( void );
/** Call this function after executing a test case to check for mutex usage
* errors. */
void mbedtls_test_mutex_usage_check( void );
#endif /* MBEDTLS_TEST_MUTEX_USAGE */
#endif /* TEST_HELPERS_H */

View File

@ -22,11 +22,20 @@
#define PSA_CRYPTO_HELPERS_H
#include "test/helpers.h"
#if defined(MBEDTLS_PSA_CRYPTO_C)
#include "test/psa_helpers.h"
#include <psa/crypto.h>
#include <psa_crypto_slot_management.h>
#if defined(MBEDTLS_USE_PSA_CRYPTO)
#include "mbedtls/psa_util.h"
#endif
#define PSA_INIT( ) PSA_ASSERT( psa_crypto_init( ) )
/** Check for things that have not been cleaned up properly in the
* PSA subsystem.
*
@ -185,4 +194,29 @@ psa_status_t mbedtls_test_record_status( psa_status_t status,
} \
while( 0 )
#endif /* MBEDTLS_PSA_CRYPTO_C */
/** \def USE_PSA_INIT
*
* Call this macro to initialize the PSA subsystem if #MBEDTLS_USE_PSA_CRYPTO
* is enabled and do nothing otherwise. If the initialization fails, mark
* the test case as failed and jump to the \p exit label.
*/
/** \def USE_PSA_DONE
*
* Call this macro at the end of a test case if you called #USE_PSA_INIT.
* This is like #PSA_DONE, except that it does nothing if
* #MBEDTLS_USE_PSA_CRYPTO is disabled.
*/
#if defined(MBEDTLS_USE_PSA_CRYPTO)
#define USE_PSA_INIT( ) PSA_INIT( )
#define USE_PSA_DONE( ) PSA_DONE( )
#else /* MBEDTLS_USE_PSA_CRYPTO */
/* Define empty macros so that we can use them in the preamble and teardown
* of every test function that uses PSA conditionally based on
* MBEDTLS_USE_PSA_CRYPTO. */
#define USE_PSA_INIT( ) ( (void) 0 )
#define USE_PSA_DONE( ) ( (void) 0 )
#endif /* !MBEDTLS_USE_PSA_CRYPTO */
#endif /* PSA_CRYPTO_HELPERS_H */

View File

@ -0,0 +1,223 @@
/** Mutex usage verification framework. */
/*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <test/helpers.h>
#include <test/macros.h>
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
#include "mbedtls/threading.h"
/** Mutex usage verification framework.
*
* The mutex usage verification code below aims to detect bad usage of
* Mbed TLS's mutex abstraction layer at runtime. Note that this is solely
* about the use of the mutex itself, not about checking whether the mutex
* correctly protects whatever it is supposed to protect.
*
* The normal usage of a mutex is:
* ```
* digraph mutex_states {
* "UNINITIALIZED"; // the initial state
* "IDLE";
* "FREED";
* "LOCKED";
* "UNINITIALIZED" -> "IDLE" [label="init"];
* "FREED" -> "IDLE" [label="init"];
* "IDLE" -> "LOCKED" [label="lock"];
* "LOCKED" -> "IDLE" [label="unlock"];
* "IDLE" -> "FREED" [label="free"];
* }
* ```
*
* All bad transitions that can be unambiguously detected are reported.
* An attempt to use an uninitialized mutex cannot be detected in general
* since the memory content may happen to denote a valid state. For the same
* reason, a double init cannot be detected.
* All-bits-zero is the state of a freed mutex, which is distinct from an
* initialized mutex, so attempting to use zero-initialized memory as a mutex
* without calling the init function is detected.
*
* The framework attempts to detect missing calls to init and free by counting
* calls to init and free. If there are more calls to init than free, this
* means that a mutex is not being freed somewhere, which is a memory leak
* on platforms where a mutex consumes resources other than the
* mbedtls_threading_mutex_t object itself. If there are more calls to free
* than init, this indicates a missing init, which is likely to be detected
* by an attempt to lock the mutex as well. A limitation of this framework is
* that it cannot detect scenarios where there is exactly the same number of
* calls to init and free but the calls don't match. A bug like this is
* unlikely to happen uniformly throughout the whole test suite though.
*
* If an error is detected, this framework will report what happened and the
* test case will be marked as failed. Unfortunately, the error report cannot
* indicate the exact location of the problematic call. To locate the error,
* use a debugger and set a breakpoint on mbedtls_test_mutex_usage_error().
*/
enum value_of_mutex_is_valid_field
{
/* Potential values for the is_valid field of mbedtls_threading_mutex_t.
* Note that MUTEX_FREED must be 0 and MUTEX_IDLE must be 1 for
* compatibility with threading_mutex_init_pthread() and
* threading_mutex_free_pthread(). MUTEX_LOCKED could be any nonzero
* value. */
MUTEX_FREED = 0, //!< Set by threading_mutex_free_pthread
MUTEX_IDLE = 1, //!< Set by threading_mutex_init_pthread and by our unlock
MUTEX_LOCKED = 2, //!< Set by our lock
};
typedef struct
{
void (*init)( mbedtls_threading_mutex_t * );
void (*free)( mbedtls_threading_mutex_t * );
int (*lock)( mbedtls_threading_mutex_t * );
int (*unlock)( mbedtls_threading_mutex_t * );
} mutex_functions_t;
static mutex_functions_t mutex_functions;
/** The total number of calls to mbedtls_mutex_init(), minus the total number
* of calls to mbedtls_mutex_free().
*
* Reset to 0 after each test case.
*/
static int live_mutexes;
static void mbedtls_test_mutex_usage_error( mbedtls_threading_mutex_t *mutex,
const char *msg )
{
(void) mutex;
if( mbedtls_test_info.mutex_usage_error == NULL )
mbedtls_test_info.mutex_usage_error = msg;
mbedtls_fprintf( stdout, "[mutex: %s] ", msg );
/* Don't mark the test as failed yet. This way, if the test fails later
* for a functional reason, the test framework will report the message
* and location for this functional reason. If the test passes,
* mbedtls_test_mutex_usage_check() will mark it as failed. */
}
static void mbedtls_test_wrap_mutex_init( mbedtls_threading_mutex_t *mutex )
{
mutex_functions.init( mutex );
if( mutex->is_valid )
++live_mutexes;
}
static void mbedtls_test_wrap_mutex_free( mbedtls_threading_mutex_t *mutex )
{
switch( mutex->is_valid )
{
case MUTEX_FREED:
mbedtls_test_mutex_usage_error( mutex, "free without init or double free" );
break;
case MUTEX_IDLE:
/* Do nothing. The underlying free function will reset is_valid
* to 0. */
break;
case MUTEX_LOCKED:
mbedtls_test_mutex_usage_error( mutex, "free without unlock" );
break;
default:
mbedtls_test_mutex_usage_error( mutex, "corrupted state" );
break;
}
if( mutex->is_valid )
--live_mutexes;
mutex_functions.free( mutex );
}
static int mbedtls_test_wrap_mutex_lock( mbedtls_threading_mutex_t *mutex )
{
int ret = mutex_functions.lock( mutex );
switch( mutex->is_valid )
{
case MUTEX_FREED:
mbedtls_test_mutex_usage_error( mutex, "lock without init" );
break;
case MUTEX_IDLE:
if( ret == 0 )
mutex->is_valid = 2;
break;
case MUTEX_LOCKED:
mbedtls_test_mutex_usage_error( mutex, "double lock" );
break;
default:
mbedtls_test_mutex_usage_error( mutex, "corrupted state" );
break;
}
return( ret );
}
static int mbedtls_test_wrap_mutex_unlock( mbedtls_threading_mutex_t *mutex )
{
int ret = mutex_functions.unlock( mutex );
switch( mutex->is_valid )
{
case MUTEX_FREED:
mbedtls_test_mutex_usage_error( mutex, "unlock without init" );
break;
case MUTEX_IDLE:
mbedtls_test_mutex_usage_error( mutex, "unlock without lock" );
break;
case MUTEX_LOCKED:
if( ret == 0 )
mutex->is_valid = MUTEX_IDLE;
break;
default:
mbedtls_test_mutex_usage_error( mutex, "corrupted state" );
break;
}
return( ret );
}
void mbedtls_test_mutex_usage_init( void )
{
mutex_functions.init = mbedtls_mutex_init;
mutex_functions.free = mbedtls_mutex_free;
mutex_functions.lock = mbedtls_mutex_lock;
mutex_functions.unlock = mbedtls_mutex_unlock;
mbedtls_mutex_init = &mbedtls_test_wrap_mutex_init;
mbedtls_mutex_free = &mbedtls_test_wrap_mutex_free;
mbedtls_mutex_lock = &mbedtls_test_wrap_mutex_lock;
mbedtls_mutex_unlock = &mbedtls_test_wrap_mutex_unlock;
}
void mbedtls_test_mutex_usage_check( void )
{
if( live_mutexes != 0 )
{
/* A positive number (more init than free) means that a mutex resource
* is leaking (on platforms where a mutex consumes more than the
* mbedtls_threading_mutex_t object itself). The rare case of a
* negative number means a missing init somewhere. */
mbedtls_fprintf( stdout, "[mutex: %d leaked] ", live_mutexes );
live_mutexes = 0;
if( mbedtls_test_info.mutex_usage_error == NULL )
mbedtls_test_info.mutex_usage_error = "missing free";
}
if( mbedtls_test_info.mutex_usage_error != NULL &&
mbedtls_test_info.result != MBEDTLS_TEST_RESULT_FAILED )
{
/* Functionally, the test passed. But there was a mutex usage error,
* so mark the test as failed after all. */
mbedtls_test_fail( "Mutex usage error", __LINE__, __FILE__ );
}
mbedtls_test_info.mutex_usage_error = NULL;
}
#endif /* MBEDTLS_TEST_MUTEX_USAGE */

View File

@ -5,9 +5,7 @@
#include <test/macros.h>
#include <test/helpers.h>
#include <test/random.h>
#if defined(MBEDTLS_PSA_CRYPTO_C)
#include <test/psa_crypto_helpers.h>
#endif
#include <stdlib.h>

View File

@ -536,6 +536,10 @@ int execute_tests( int argc , const char ** argv )
mbedtls_memory_buffer_alloc_init( alloc_buf, sizeof( alloc_buf ) );
#endif
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
mbedtls_test_mutex_usage_init( );
#endif
/*
* The C standard doesn't guarantee that all-bits-0 is the representation
* of a NULL pointer. We do however use that in our code for initializing

View File

@ -188,6 +188,10 @@ void execute_function_ptr(TestWrapper_t fp, void **params)
#else
fp( params );
#endif
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
mbedtls_test_mutex_usage_check( );
#endif /* MBEDTLS_TEST_MUTEX_USAGE */
}
/**

View File

@ -1,3 +1,9 @@
Entropy init-free-free
entropy_init_free:0
Entropy init-free-init-free
entropy_init_free:1
Create NV seed_file
nv_seed_file_create:

View File

@ -134,6 +134,28 @@ int read_nv_seed( unsigned char *buf, size_t buf_len )
* END_DEPENDENCIES
*/
/* BEGIN_CASE */
void entropy_init_free( int reinit )
{
mbedtls_entropy_context ctx;
/* Double free is not explicitly documented to work, but it is convenient
* to call mbedtls_entropy_free() unconditionally on an error path without
* checking whether it has already been called in the success path. */
mbedtls_entropy_init( &ctx );
mbedtls_entropy_free( &ctx );
if( reinit )
mbedtls_entropy_init( &ctx );
mbedtls_entropy_free( &ctx );
/* This test case always succeeds, functionally speaking. A plausible
* bug might trigger an invalid pointer dereference or a memory leak. */
goto exit;
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */
void entropy_seed_file( char * path, int ret )
{
@ -217,6 +239,9 @@ void entropy_func_len( int len, int ret )
for( j = len; j < sizeof( buf ); j++ )
TEST_ASSERT( acc[j] == 0 );
exit:
mbedtls_entropy_free( &ctx );
}
/* END_CASE */

View File

@ -15,18 +15,6 @@
* unconditionally (https://github.com/ARMmbed/mbedtls/issues/2023). */
#include "psa/crypto.h"
#if defined(MBEDTLS_USE_PSA_CRYPTO)
#include "mbedtls/psa_util.h"
#define PSA_INIT( ) PSA_ASSERT( psa_crypto_init( ) )
#else
/* Define empty macros so that we can use them in the preamble and teardown
* of every test function that uses PSA conditionally based on
* MBEDTLS_USE_PSA_CRYPTO. */
#define PSA_INIT( ) ( (void) 0 )
#undef PSA_DONE
#define PSA_DONE( ) ( (void) 0 )
#endif
#define RSA_KEY_SIZE 512
#define RSA_KEY_LEN 64
@ -208,7 +196,7 @@ exit:
mbedtls_pk_free( &pk ); /* redundant except upon error */
mbedtls_pk_free( &pk2 );
PSA_DONE( );
USE_PSA_DONE( );
}
/* END_CASE */
@ -770,7 +758,7 @@ void pk_ec_test_vec( int type, int id, data_t * key, data_t * hash,
mbedtls_ecp_keypair *eckey;
mbedtls_pk_init( &pk );
PSA_INIT( );
USE_PSA_INIT( );
TEST_ASSERT( mbedtls_pk_setup( &pk, mbedtls_pk_info_from_type( type ) ) == 0 );
@ -787,7 +775,7 @@ void pk_ec_test_vec( int type, int id, data_t * key, data_t * hash,
exit:
mbedtls_pk_free( &pk );
PSA_DONE( );
USE_PSA_DONE( );
}
/* END_CASE */
@ -911,7 +899,7 @@ void pk_sign_verify( int type, int parameter, int sign_ret, int verify_ret )
#endif
mbedtls_pk_init( &pk );
PSA_INIT( );
USE_PSA_INIT( );
memset( hash, 0x2a, sizeof hash );
memset( sig, 0, sizeof sig );
@ -973,7 +961,7 @@ exit:
mbedtls_pk_restart_free( rs_ctx );
#endif
mbedtls_pk_free( &pk );
PSA_DONE( );
USE_PSA_DONE( );
}
/* END_CASE */
@ -1302,6 +1290,6 @@ exit:
psa_reset_key_attributes( &attributes );
mbedtls_pk_free( &pk );
PSA_DONE( );
USE_PSA_DONE( );
}
/* END_CASE */

View File

@ -1,6 +1,12 @@
RSA parameter validation
rsa_invalid_param:
RSA init-free-free
rsa_init_free:0
RSA init-free-init-free
rsa_init_free:1
RSA PKCS1 Verify v1.5 CAVS #1
depends_on:MBEDTLS_SHA1_C:MBEDTLS_PKCS1_V15
# Good padding but wrong hash

View File

@ -466,6 +466,29 @@ exit:
}
/* END_CASE */
/* BEGIN_CASE */
void rsa_init_free( int reinit )
{
mbedtls_rsa_context ctx;
/* Double free is not explicitly documented to work, but we rely on it
* even inside the library so that you can call mbedtls_rsa_free()
* unconditionally on an error path without checking whether it has
* already been called in the success path. */
mbedtls_rsa_init( &ctx, 0, 0 );
mbedtls_rsa_free( &ctx );
if( reinit )
mbedtls_rsa_init( &ctx, 0, 0 );
mbedtls_rsa_free( &ctx );
/* This test case always succeeds, functionally speaking. A plausible
* bug might trigger an invalid pointer dereference or a memory leak. */
goto exit;
}
/* END_CASE */
/* BEGIN_CASE */
void mbedtls_rsa_pkcs1_sign( data_t * message_str, int padding_mode,
int digest, int mod, int radix_P, char * input_P,

View File

@ -3836,9 +3836,7 @@ void ssl_tls_prf( int type, data_t * secret, data_t * random,
if( output == NULL )
goto exit;
#if defined(MBEDTLS_USE_PSA_CRYPTO)
TEST_ASSERT( psa_crypto_init() == 0 );
#endif
USE_PSA_INIT( );
TEST_ASSERT( mbedtls_ssl_tls_prf( type, secret->x, secret->len,
label, random->x, random->len,
@ -3852,6 +3850,7 @@ void ssl_tls_prf( int type, data_t * secret, data_t * random,
exit:
mbedtls_free( output );
USE_PSA_DONE( );
}
/* END_CASE */

View File

@ -610,14 +610,12 @@ void x509_verify( char *crt_file, char *ca_file, char *crl_file,
char * cn_name = NULL;
const mbedtls_x509_crt_profile *profile;
#if defined(MBEDTLS_USE_PSA_CRYPTO)
TEST_ASSERT( psa_crypto_init() == 0 );
#endif
mbedtls_x509_crt_init( &crt );
mbedtls_x509_crt_init( &ca );
mbedtls_x509_crl_init( &crl );
USE_PSA_INIT( );
if( strcmp( cn_name_str, "NULL" ) != 0 )
cn_name = cn_name_str;
@ -669,6 +667,7 @@ exit:
mbedtls_x509_crt_free( &crt );
mbedtls_x509_crt_free( &ca );
mbedtls_x509_crl_free( &crl );
USE_PSA_DONE( );
}
/* END_CASE */
@ -712,14 +711,12 @@ void x509_verify_callback( char *crt_file, char *ca_file, char *name,
uint32_t flags = 0;
verify_print_context vrfy_ctx;
#if defined(MBEDTLS_USE_PSA_CRYPTO)
TEST_ASSERT( psa_crypto_init() == 0 );
#endif
mbedtls_x509_crt_init( &crt );
mbedtls_x509_crt_init( &ca );
verify_print_init( &vrfy_ctx );
USE_PSA_INIT( );
TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == 0 );
TEST_ASSERT( mbedtls_x509_crt_parse_file( &ca, ca_file ) == 0 );
@ -737,6 +734,7 @@ void x509_verify_callback( char *crt_file, char *ca_file, char *name,
exit:
mbedtls_x509_crt_free( &crt );
mbedtls_x509_crt_free( &ca );
USE_PSA_DONE( );
}
/* END_CASE */
@ -1024,10 +1022,6 @@ void mbedtls_x509_crt_verify_max( char *ca_file, char *chain_dir, int nb_int,
uint32_t flags;
mbedtls_x509_crt trusted, chain;
#if defined(MBEDTLS_USE_PSA_CRYPTO)
TEST_ASSERT( psa_crypto_init() == 0 );
#endif
/*
* We expect chain_dir to contain certificates 00.crt, 01.crt, etc.
* with NN.crt signed by NN-1.crt
@ -1036,6 +1030,8 @@ void mbedtls_x509_crt_verify_max( char *ca_file, char *chain_dir, int nb_int,
mbedtls_x509_crt_init( &trusted );
mbedtls_x509_crt_init( &chain );
USE_PSA_INIT( );
/* Load trusted root */
TEST_ASSERT( mbedtls_x509_crt_parse_file( &trusted, ca_file ) == 0 );
@ -1055,6 +1051,7 @@ void mbedtls_x509_crt_verify_max( char *ca_file, char *chain_dir, int nb_int,
exit:
mbedtls_x509_crt_free( &chain );
mbedtls_x509_crt_free( &trusted );
USE_PSA_DONE( );
}
/* END_CASE */
@ -1069,13 +1066,11 @@ void mbedtls_x509_crt_verify_chain( char *chain_paths, char *trusted_ca,
mbedtls_x509_crt trusted, chain;
const mbedtls_x509_crt_profile *profile = NULL;
#if defined(MBEDTLS_USE_PSA_CRYPTO)
TEST_ASSERT( psa_crypto_init() == 0 );
#endif
mbedtls_x509_crt_init( &chain );
mbedtls_x509_crt_init( &trusted );
USE_PSA_INIT( );
while( ( act = mystrsep( &chain_paths, " " ) ) != NULL )
TEST_ASSERT( mbedtls_x509_crt_parse_file( &chain, act ) == 0 );
TEST_ASSERT( mbedtls_x509_crt_parse_file( &trusted, trusted_ca ) == 0 );
@ -1100,6 +1095,7 @@ void mbedtls_x509_crt_verify_chain( char *chain_paths, char *trusted_ca,
exit:
mbedtls_x509_crt_free( &trusted );
mbedtls_x509_crt_free( &chain );
USE_PSA_DONE( );
}
/* END_CASE */

View File

@ -6,12 +6,6 @@
#include "mbedtls/oid.h"
#include "mbedtls/rsa.h"
#if defined(MBEDTLS_USE_PSA_CRYPTO)
#include "psa/crypto.h"
#include "mbedtls/psa_util.h"
#define PSA_INIT( ) PSA_ASSERT( psa_crypto_init( ) )
#endif /* MBEDTLS_USE_PSA_CRYPTO */
#if defined(MBEDTLS_RSA_C)
int mbedtls_rsa_decrypt_func( void *ctx, int mode, size_t *olen,
const unsigned char *input, unsigned char *output,

View File

@ -358,6 +358,7 @@
<ClCompile Include="..\..\tests\src\helpers.c" />
<ClCompile Include="..\..\tests\src\psa_crypto_helpers.c" />
<ClCompile Include="..\..\tests\src\random.c" />
<ClCompile Include="..\..\tests\src\threading_helpers.c" />
<ClCompile Include="..\..\3rdparty\everest\library\everest.c" />
<ClCompile Include="..\..\3rdparty\everest\library\Hacl_Curve25519_joined.c" />
<ClCompile Include="..\..\3rdparty\everest\library\x25519.c" />