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 <Gilles.Peskine@arm.com>
diff --git a/tests/src/threading_helpers.c b/tests/src/threading_helpers.c
index 3a58bc7..8cf95ee 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 @@
 } 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_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 @@
             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_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 )
     {