From f96d3d8b204a8fc407534a52a1fe4f3dcf7b3161 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 29 Jan 2021 22:20:32 +0100 Subject: [PATCH] Count and report non-freed mutexes Subtract the number of calls to mbedtls_mutex_free() from the number of calls to mbedtls_mutex_init(). A mutex leak will manifest as a positive result at the end of the test case. Signed-off-by: Gilles Peskine --- tests/src/threading_helpers.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/src/threading_helpers.c b/tests/src/threading_helpers.c index 3a58bc731..8cf95ee33 100644 --- a/tests/src/threading_helpers.c +++ b/tests/src/threading_helpers.c @@ -54,6 +54,17 @@ * 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, @@ -75,6 +86,13 @@ typedef struct } 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 ) { @@ -91,6 +109,8 @@ static void mbedtls_test_mutex_usage_error( mbedtls_threading_mutex_t *mutex, 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 ) @@ -111,6 +131,8 @@ static void mbedtls_test_wrap_mutex_free( mbedtls_threading_mutex_t *mutex ) mbedtls_test_mutex_usage_error( mutex, "corrupted state" ); break; } + if( mutex->is_valid ) + --live_mutexes; mutex_functions.free( mutex ); } @@ -172,6 +194,17 @@ void mbedtls_test_mutex_usage_init( void ) 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 ) {