aboutsummaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorPaul Elliott <62069445+paul-elliott-arm@users.noreply.github.com>2024-03-15 19:50:01 +0000
committerGitHub <noreply@github.com>2024-03-15 19:50:01 +0000
commit78064ac9e09d8f32bac736f3d5f5153ce7abc0dd (patch)
tree5794256ba538c4b2e515981a32965b13089067c7 /library
parentc9db499299bcd49f7016f1a4d11a6f934628d166 (diff)
parentb24e36d07b16e2e4c7f6341f5e033ed7f4ac773f (diff)
downloadmbedtls-78064ac9e09d8f32bac736f3d5f5153ce7abc0dd.zip
mbedtls-78064ac9e09d8f32bac736f3d5f5153ce7abc0dd.tar.gz
mbedtls-78064ac9e09d8f32bac736f3d5f5153ce7abc0dd.tar.bz2
Merge pull request #8901 from paul-elliott-arm/make_psa_global_data_safe
Make PSA global_data thread safe
Diffstat (limited to 'library')
-rw-r--r--library/psa_crypto.c317
-rw-r--r--library/psa_crypto_slot_management.c26
-rw-r--r--library/threading.c6
3 files changed, 304 insertions, 45 deletions
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index ec9d115..a0a002a 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) || \
@@ -92,30 +93,93 @@ static int key_type_is_raw_bytes(psa_key_type_t type)
#define RNG_INITIALIZED 1
#define RNG_SEEDED 2
+/* IDs for PSA crypto subsystems. Starts at 1 to catch potential uninitialized
+ * variables as arguments. */
+typedef enum {
+ PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS = 1,
+ PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS,
+ PSA_CRYPTO_SUBSYSTEM_RNG,
+ PSA_CRYPTO_SUBSYSTEM_TRANSACTION,
+} mbedtls_psa_crypto_subsystem;
+
+/* Initialization flags for global_data::initialized */
+#define PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED 0x01
+#define PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED 0x02
+#define PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED 0x04
+
+#define PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED ( \
+ PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED | \
+ PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED | \
+ PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)
+
typedef struct {
uint8_t initialized;
uint8_t rng_state;
- uint8_t drivers_initialized;
mbedtls_psa_random_context_t rng;
} psa_global_data_t;
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_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ initialized = global_data.rng_state == RNG_SEEDED;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ initialized =
+ (initialized && (global_data.initialized == PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED));
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return initialized;
+}
+
+static uint8_t psa_get_drivers_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 & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) != 0;
+
+#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)
{
(void) hash_alg;
- return global_data.drivers_initialized;
+ return psa_get_drivers_initialized();
}
int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg)
{
(void) key_type;
(void) cipher_alg;
- return global_data.drivers_initialized;
+ return psa_get_drivers_initialized();
}
@@ -7082,6 +7146,9 @@ exit:
#endif
/** Initialize the PSA random generator.
+ *
+ * Note: the mbedtls_threading_psa_rngdata_mutex should be held when calling
+ * this function if mutexes are enabled.
*/
static void mbedtls_psa_random_init(mbedtls_psa_random_context_t *rng)
{
@@ -7114,6 +7181,9 @@ static void mbedtls_psa_random_init(mbedtls_psa_random_context_t *rng)
}
/** Deinitialize the PSA random generator.
+ *
+ * Note: the mbedtls_threading_psa_rngdata_mutex should be held when calling
+ * this function if mutexes are enabled.
*/
static void mbedtls_psa_random_free(mbedtls_psa_random_context_t *rng)
{
@@ -7189,7 +7259,7 @@ psa_status_t psa_generate_random(uint8_t *output,
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;
}
@@ -7431,28 +7501,77 @@ psa_status_t mbedtls_psa_crypto_configure_entropy_sources(
void (* entropy_init)(mbedtls_entropy_context *ctx),
void (* entropy_free)(mbedtls_entropy_context *ctx))
{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
if (global_data.rng_state != RNG_NOT_INITIALIZED) {
- return PSA_ERROR_BAD_STATE;
+ status = PSA_ERROR_BAD_STATE;
+ } else {
+ global_data.rng.entropy_init = entropy_init;
+ global_data.rng.entropy_free = entropy_free;
+ status = PSA_SUCCESS;
}
- global_data.rng.entropy_init = entropy_init;
- global_data.rng.entropy_free = entropy_free;
- return PSA_SUCCESS;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return status;
}
#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
void mbedtls_psa_crypto_free(void)
{
- psa_wipe_all_key_slots();
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Nothing to do to free transaction. */
+ if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED) {
+ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ }
+
+ if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED) {
+ psa_wipe_all_key_slots();
+ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
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));
+ global_data.rng_state = RNG_NOT_INITIALIZED;
+ mbedtls_platform_zeroize(&global_data.rng, sizeof(global_data.rng));
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
/* Terminate drivers */
- psa_driver_wrapper_free();
+ if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) {
+ psa_driver_wrapper_free();
+ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
}
#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
@@ -7480,57 +7599,171 @@ static psa_status_t psa_crypto_recover_transaction(
}
#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
+static psa_status_t mbedtls_psa_crypto_init_subsystem(mbedtls_psa_crypto_subsystem subsystem)
+{
+ psa_status_t status = PSA_SUCCESS;
+ uint8_t driver_wrappers_initialized = 0;
+
+ switch (subsystem) {
+ case PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED)) {
+ /* Init drivers */
+ status = psa_driver_wrapper_init();
+
+ /* Drivers need shutdown regardless of startup errors. */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
+
+
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ case PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED)) {
+ status = psa_initialize_key_slots();
+
+ /* Need to wipe keys even if initialization fails. */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
+
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ case PSA_CRYPTO_SUBSYSTEM_RNG:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ driver_wrappers_initialized =
+ (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED);
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Need to use separate mutex here, as initialisation can require
+ * testing of init flags, which requires locking the global data
+ * mutex. */
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Initialize and seed the random generator. */
+ if (global_data.rng_state == RNG_NOT_INITIALIZED && driver_wrappers_initialized) {
+ mbedtls_psa_random_init(&global_data.rng);
+ global_data.rng_state = RNG_INITIALIZED;
+
+ status = mbedtls_psa_random_seed(&global_data.rng);
+ if (status == PSA_SUCCESS) {
+ global_data.rng_state = RNG_SEEDED;
+ }
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_rngdata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ case PSA_CRYPTO_SUBSYSTEM_TRANSACTION:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)) {
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+ status = psa_crypto_load_transaction();
+ if (status == PSA_SUCCESS) {
+ status = psa_crypto_recover_transaction(&psa_crypto_transaction);
+ if (status == PSA_SUCCESS) {
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ }
+ status = psa_crypto_stop_transaction();
+ } else if (status == PSA_ERROR_DOES_NOT_EXIST) {
+ /* There's no transaction to complete. It's all good. */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ status = PSA_SUCCESS;
+ }
+#else /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ status = PSA_SUCCESS;
+#endif /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ default:
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ /* Exit label only required when using threading macros. */
+#if defined(MBEDTLS_THREADING_C)
+exit:
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return status;
+}
+
psa_status_t psa_crypto_init(void)
{
psa_status_t status;
- /* Double initialization is explicitly allowed. */
- if (global_data.initialized != 0) {
+ /* Double initialization is explicitly allowed. Early out if everything is
+ * done. */
+ if (psa_get_initialized()) {
return PSA_SUCCESS;
}
- /* Init drivers */
- status = psa_driver_wrapper_init();
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS);
if (status != PSA_SUCCESS) {
goto exit;
}
- global_data.drivers_initialized = 1;
- status = psa_initialize_key_slots();
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS);
if (status != PSA_SUCCESS) {
goto exit;
}
- /* Initialize and seed the random generator. */
- mbedtls_psa_random_init(&global_data.rng);
- global_data.rng_state = RNG_INITIALIZED;
- status = mbedtls_psa_random_seed(&global_data.rng);
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_RNG);
if (status != PSA_SUCCESS) {
goto exit;
}
- global_data.rng_state = RNG_SEEDED;
-
-#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
- status = psa_crypto_load_transaction();
- if (status == PSA_SUCCESS) {
- status = psa_crypto_recover_transaction(&psa_crypto_transaction);
- if (status != PSA_SUCCESS) {
- goto exit;
- }
- status = psa_crypto_stop_transaction();
- } else if (status == PSA_ERROR_DOES_NOT_EXIST) {
- /* There's no transaction to complete. It's all good. */
- status = PSA_SUCCESS;
- }
-#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
- /* All done. */
- global_data.initialized = 1;
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_TRANSACTION);
exit:
+
if (status != PSA_SUCCESS) {
mbedtls_psa_crypto_free();
}
+
return status;
}
diff --git a/library/psa_crypto_slot_management.c b/library/psa_crypto_slot_management.c
index 5dee32f..b184ed0 100644
--- a/library/psa_crypto_slot_management.c
+++ b/library/psa_crypto_slot_management.c
@@ -34,6 +34,23 @@ typedef struct {
static psa_global_data_t global_data;
+static uint8_t psa_get_key_slots_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.key_slots_initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return initialized;
+}
+
int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok)
{
psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
@@ -136,7 +153,9 @@ psa_status_t psa_initialize_key_slots(void)
{
/* Nothing to do: program startup and psa_wipe_all_key_slots() both
* guarantee that the key slots are initialized to all-zero, which
- * means that all the key slots are in a valid, empty state. */
+ * means that all the key slots are in a valid, empty state. The global
+ * data mutex is already held when calling this function, so no need to
+ * lock it here, to set the flag. */
global_data.key_slots_initialized = 1;
return PSA_SUCCESS;
}
@@ -151,6 +170,7 @@ void psa_wipe_all_key_slots(void)
slot->state = PSA_SLOT_PENDING_DELETION;
(void) psa_wipe_key_slot(slot);
}
+ /* The global data mutex is already held when calling this function. */
global_data.key_slots_initialized = 0;
}
@@ -161,7 +181,7 @@ psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
size_t slot_idx;
psa_key_slot_t *selected_slot, *unused_persistent_key_slot;
- if (!global_data.key_slots_initialized) {
+ if (!psa_get_key_slots_initialized()) {
status = PSA_ERROR_BAD_STATE;
goto error;
}
@@ -344,7 +364,7 @@ psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key,
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
*p_slot = NULL;
- if (!global_data.key_slots_initialized) {
+ if (!psa_get_key_slots_initialized()) {
return PSA_ERROR_BAD_STATE;
}
diff --git a/library/threading.c b/library/threading.c
index c28290f..85db243 100644
--- a/library/threading.c
+++ b/library/threading.c
@@ -150,6 +150,8 @@ void mbedtls_threading_set_alt(void (*mutex_init)(mbedtls_threading_mutex_t *),
#endif
#if defined(MBEDTLS_PSA_CRYPTO_C)
mbedtls_mutex_init(&mbedtls_threading_key_slot_mutex);
+ mbedtls_mutex_init(&mbedtls_threading_psa_globaldata_mutex);
+ mbedtls_mutex_init(&mbedtls_threading_psa_rngdata_mutex);
#endif
}
@@ -166,6 +168,8 @@ void mbedtls_threading_free_alt(void)
#endif
#if defined(MBEDTLS_PSA_CRYPTO_C)
mbedtls_mutex_free(&mbedtls_threading_key_slot_mutex);
+ mbedtls_mutex_free(&mbedtls_threading_psa_globaldata_mutex);
+ mbedtls_mutex_free(&mbedtls_threading_psa_rngdata_mutex);
#endif
}
#endif /* MBEDTLS_THREADING_ALT */
@@ -184,6 +188,8 @@ mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex MUTEX_INIT;
#endif
#if defined(MBEDTLS_PSA_CRYPTO_C)
mbedtls_threading_mutex_t mbedtls_threading_key_slot_mutex MUTEX_INIT;
+mbedtls_threading_mutex_t mbedtls_threading_psa_globaldata_mutex MUTEX_INIT;
+mbedtls_threading_mutex_t mbedtls_threading_psa_rngdata_mutex MUTEX_INIT;
#endif
#endif /* MBEDTLS_THREADING_C */