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

Test and fix mutex usage
diff --git a/ChangeLog.d/drbg-mutex.txt b/ChangeLog.d/drbg-mutex.txt
new file mode 100644
index 0000000..3ac5abf
--- /dev/null
+++ b/ChangeLog.d/drbg-mutex.txt
@@ -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.
diff --git a/ChangeLog.d/rsa-mutex.txt b/ChangeLog.d/rsa-mutex.txt
new file mode 100644
index 0000000..2a477a9
--- /dev/null
+++ b/ChangeLog.d/rsa-mutex.txt
@@ -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.
diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h
index 7f1d232..653fd83 100644
--- a/include/mbedtls/ctr_drbg.h
+++ b/include/mbedtls/ctr_drbg.h
@@ -200,6 +200,13 @@
     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 @@
  *   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 @@
  *                      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 @@
  * \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 @@
 /**
  * \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 @@
  * 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 @@
  *
  * 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.
diff --git a/include/mbedtls/entropy.h b/include/mbedtls/entropy.h
index 5a9c11c..fa0b24f 100644
--- a/include/mbedtls/entropy.h
+++ b/include/mbedtls/entropy.h
@@ -120,13 +120,15 @@
  */
 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;
diff --git a/include/mbedtls/hmac_drbg.h b/include/mbedtls/hmac_drbg.h
index 9116541..fa33611 100644
--- a/include/mbedtls/hmac_drbg.h
+++ b/include/mbedtls/hmac_drbg.h
@@ -101,6 +101,14 @@
     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 @@
  * \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 @@
  *
  * 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 @@
 /**
  * \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 @@
  * \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 @@
  * 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 @@
  *
  * 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.
diff --git a/include/mbedtls/rsa.h b/include/mbedtls/rsa.h
index 6a31514..701fe8b 100644
--- a/include/mbedtls/rsa.h
+++ b/include/mbedtls/rsa.h
@@ -97,7 +97,10 @@
  */
 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 @@
                                      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
 }
diff --git a/include/mbedtls/threading.h b/include/mbedtls/threading.h
index 8baf15a..05e27c5 100644
--- a/include/mbedtls/threading.h
+++ b/include/mbedtls/threading.h
@@ -46,6 +46,9 @@
 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
diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c
index 3815dc7..ab52861 100644
--- a/library/ctr_drbg.c
+++ b/library/ctr_drbg.c
@@ -56,10 +56,6 @@
     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 @@
         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 @@
 
     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;
diff --git a/library/entropy.c b/library/entropy.c
index db61f16..b9aca86 100644
--- a/library/entropy.c
+++ b/library/entropy.c
@@ -116,6 +116,11 @@
 
 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 @@
 #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,
diff --git a/library/hmac_drbg.c b/library/hmac_drbg.c
index 25a0225..de97068 100644
--- a/library/hmac_drbg.c
+++ b/library/hmac_drbg.c
@@ -54,10 +54,6 @@
     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 @@
     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 @@
     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 @@
         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)
diff --git a/library/rsa.c b/library/rsa.c
index 9fe551d..68a36f2 100644
--- a/library/rsa.c
+++ b/library/rsa.c
@@ -490,6 +490,9 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
 #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
 }
 
diff --git a/library/threading.c b/library/threading.c
index 2bb932d..2de117f 100644
--- a/library/threading.c
+++ b/library/threading.c
@@ -67,6 +67,12 @@
     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;
 }
 
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 4875b78..d0758bc 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -734,6 +734,10 @@
     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 @@
     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();
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 08317b5..bd4dbb6 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -1369,6 +1369,10 @@
 #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 @@
     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();
diff --git a/programs/ssl/ssl_test_lib.c b/programs/ssl/ssl_test_lib.c
index 6636e9e..1bb9d61 100644
--- a/programs/ssl/ssl_test_lib.c
+++ b/programs/ssl/ssl_test_lib.c
@@ -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 @@
     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) */
diff --git a/programs/ssl/ssl_test_lib.h b/programs/ssl/ssl_test_lib.h
index 04ba158..98751a0 100644
--- a/programs/ssl/ssl_test_lib.h
+++ b/programs/ssl/ssl_test_lib.h
@@ -258,5 +258,37 @@
 #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 */
diff --git a/tests/include/test/helpers.h b/tests/include/test/helpers.h
index 928c636..c3a844b 100644
--- a/tests/include/test/helpers.h
+++ b/tests/include/test/helpers.h
@@ -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 @@
     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 @@
 #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 */
diff --git a/tests/include/test/psa_crypto_helpers.h b/tests/include/test/psa_crypto_helpers.h
index b7dc4b5..30bb20f 100644
--- a/tests/include/test/psa_crypto_helpers.h
+++ b/tests/include/test/psa_crypto_helpers.h
@@ -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 @@
     }                                                                      \
     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 */
diff --git a/tests/src/threading_helpers.c b/tests/src/threading_helpers.c
new file mode 100644
index 0000000..ca91b79
--- /dev/null
+++ b/tests/src/threading_helpers.c
@@ -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 */
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index ebe2f06..91ad925 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -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>
 
diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function
index 3138c33..f4f4f45 100644
--- a/tests/suites/host_test.function
+++ b/tests/suites/host_test.function
@@ -536,6 +536,10 @@
     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
diff --git a/tests/suites/main_test.function b/tests/suites/main_test.function
index 57395ae..aa408ea 100644
--- a/tests/suites/main_test.function
+++ b/tests/suites/main_test.function
@@ -188,6 +188,10 @@
 #else
     fp( params );
 #endif
+
+#if defined(MBEDTLS_TEST_MUTEX_USAGE)
+    mbedtls_test_mutex_usage_check( );
+#endif /* MBEDTLS_TEST_MUTEX_USAGE */
 }
 
 /**
diff --git a/tests/suites/test_suite_entropy.data b/tests/suites/test_suite_entropy.data
index b2d20b4..2ce01fa 100644
--- a/tests/suites/test_suite_entropy.data
+++ b/tests/suites/test_suite_entropy.data
@@ -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:
 
diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function
index d9ea441..ad9b5a4 100644
--- a/tests/suites/test_suite_entropy.function
+++ b/tests/suites/test_suite_entropy.function
@@ -134,6 +134,28 @@
  * 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 @@
 
     for( j = len; j < sizeof( buf ); j++ )
         TEST_ASSERT( acc[j] == 0 );
+
+exit:
+    mbedtls_entropy_free( &ctx );
 }
 /* END_CASE */
 
diff --git a/tests/suites/test_suite_pk.function b/tests/suites/test_suite_pk.function
index 577fb47..bc469b6 100644
--- a/tests/suites/test_suite_pk.function
+++ b/tests/suites/test_suite_pk.function
@@ -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 @@
 
     mbedtls_pk_free( &pk ); /* redundant except upon error */
     mbedtls_pk_free( &pk2 );
-    PSA_DONE( );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
@@ -770,7 +758,7 @@
     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 @@
 
 exit:
     mbedtls_pk_free( &pk );
-    PSA_DONE( );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
@@ -911,7 +899,7 @@
 #endif
 
     mbedtls_pk_init( &pk );
-    PSA_INIT( );
+    USE_PSA_INIT( );
 
     memset( hash, 0x2a, sizeof hash );
     memset( sig, 0, sizeof sig );
@@ -973,7 +961,7 @@
     mbedtls_pk_restart_free( rs_ctx );
 #endif
     mbedtls_pk_free( &pk );
-    PSA_DONE( );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
@@ -1302,6 +1290,6 @@
     psa_reset_key_attributes( &attributes );
 
     mbedtls_pk_free( &pk );
-    PSA_DONE( );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
diff --git a/tests/suites/test_suite_rsa.data b/tests/suites/test_suite_rsa.data
index 30919f3..6f9406c 100644
--- a/tests/suites/test_suite_rsa.data
+++ b/tests/suites/test_suite_rsa.data
@@ -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
diff --git a/tests/suites/test_suite_rsa.function b/tests/suites/test_suite_rsa.function
index 6c73e39..cdbaa13 100644
--- a/tests/suites/test_suite_rsa.function
+++ b/tests/suites/test_suite_rsa.function
@@ -467,6 +467,29 @@
 /* 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,
                              int radix_Q, char * input_Q, int radix_N,
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 5c97d90..b1ebf5b 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -3836,9 +3836,7 @@
     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 @@
 exit:
 
     mbedtls_free( output );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function
index 2bba4e2..66f0376 100644
--- a/tests/suites/test_suite_x509parse.function
+++ b/tests/suites/test_suite_x509parse.function
@@ -610,14 +610,12 @@
     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 @@
     mbedtls_x509_crt_free( &crt );
     mbedtls_x509_crt_free( &ca );
     mbedtls_x509_crl_free( &crl );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
@@ -712,14 +711,12 @@
     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 @@
 exit:
     mbedtls_x509_crt_free( &crt );
     mbedtls_x509_crt_free( &ca );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
@@ -1024,10 +1022,6 @@
     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 @@
     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 @@
 exit:
     mbedtls_x509_crt_free( &chain );
     mbedtls_x509_crt_free( &trusted );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
@@ -1069,13 +1066,11 @@
     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 @@
 exit:
     mbedtls_x509_crt_free( &trusted );
     mbedtls_x509_crt_free( &chain );
+    USE_PSA_DONE( );
 }
 /* END_CASE */
 
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index 9960989..59ea17b 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -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,
diff --git a/visualc/VS2010/mbedTLS.vcxproj b/visualc/VS2010/mbedTLS.vcxproj
index e6d6532..da026d9 100644
--- a/visualc/VS2010/mbedTLS.vcxproj
+++ b/visualc/VS2010/mbedTLS.vcxproj
@@ -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" />