Protect PSA global initialized flag with mutex.

Unfortunately this requires holding the mutex for the entire
psa_crypto_init() function, as calling psa_crypto_free() from another
thread should block until init has ended, then run.

Signed-off-by: Paul Elliott <paul.elliott@arm.com>
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index ec9d115..3019522 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -71,6 +71,7 @@
 #include "mbedtls/sha256.h"
 #include "mbedtls/sha512.h"
 #include "mbedtls/psa_util.h"
+#include "mbedtls/threading.h"
 
 #if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) ||          \
     defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) ||  \
@@ -101,8 +102,25 @@
 
 static psa_global_data_t global_data;
 
+static uint8_t psa_get_initialized(void)
+{
+    uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    initialized = global_data.initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    return initialized;
+}
+
 #define GUARD_MODULE_INITIALIZED        \
-    if (global_data.initialized == 0)  \
+    if (psa_get_initialized() == 0)     \
     return PSA_ERROR_BAD_STATE;
 
 int psa_can_do_hash(psa_algorithm_t hash_alg)
@@ -7189,7 +7207,7 @@
 psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed,
                                         size_t seed_size)
 {
-    if (global_data.initialized) {
+    if (psa_get_initialized()) {
         return PSA_ERROR_NOT_PERMITTED;
     }
 
@@ -7442,15 +7460,27 @@
 
 void mbedtls_psa_crypto_free(void)
 {
+    /* Need to hold the mutex here to prevent this going ahead before
+     * psa_crypto_init() has completed, and to ensure integrity of
+     * global_data. */
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
     psa_wipe_all_key_slots();
     if (global_data.rng_state != RNG_NOT_INITIALIZED) {
         mbedtls_psa_random_free(&global_data.rng);
     }
+
     /* Wipe all remaining data, including configuration.
      * In particular, this sets all state indicator to the value
      * indicating "uninitialized". */
     mbedtls_platform_zeroize(&global_data, sizeof(global_data));
 
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
     /* Terminate drivers */
     psa_driver_wrapper_free();
 }
@@ -7484,8 +7514,20 @@
 {
     psa_status_t status;
 
-    /* Double initialization is explicitly allowed. */
-    if (global_data.initialized != 0) {
+    /* Need to hold the mutex for the entire function to prevent incomplete
+     * initialisation before someone calls psa_crypto_free() or calls this
+     * function again before we set global_data.initialised to 1. */
+    #if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+    /* We cannot use psa_get_initialized() here as we already hold the mutex. */
+    if (global_data.initialized == 1) {
+#if defined(MBEDTLS_THREADING_C)
+        mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+        /* Double initialization is explicitly allowed. */
         return PSA_SUCCESS;
     }
 
@@ -7528,6 +7570,11 @@
     global_data.initialized = 1;
 
 exit:
+
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
     if (status != PSA_SUCCESS) {
         mbedtls_psa_crypto_free();
     }