psa_key_slot_t: different fields in free vs occupied slots

Place some fields of psa_key_slot_t in a union, to prepare for a new field
in free slots that should not require extra memory.

For occupied slots, place only the registered_readers field in the union,
not other fields, to minimize textual changes. All fields could move to the
union except state (also needed in free slots) and attr (which must stay
first to reduce the code size, because it is accessed at many call sites).

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
Gilles Peskine 2024-06-10 11:42:41 +02:00
parent b5891cc2cd
commit 47ad2f7484
4 changed files with 46 additions and 38 deletions

View File

@ -1210,15 +1210,15 @@ psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot)
case PSA_SLOT_PENDING_DELETION:
/* In this state psa_wipe_key_slot() must only be called if the
* caller is the last reader. */
if (slot->registered_readers != 1) {
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 1);
if (slot->var.occupied.registered_readers != 1) {
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->var.occupied.registered_readers == 1);
status = PSA_ERROR_CORRUPTION_DETECTED;
}
break;
case PSA_SLOT_FILLING:
/* In this state registered_readers must be 0. */
if (slot->registered_readers != 0) {
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 0);
if (slot->var.occupied.registered_readers != 0) {
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->var.occupied.registered_readers == 0);
status = PSA_ERROR_CORRUPTION_DETECTED;
}
break;

View File

@ -59,6 +59,8 @@ typedef enum {
* and metadata for one key.
*/
typedef struct {
/* This field is accessed in a lot of places. Putting it first
* reduces the code size. */
psa_key_attributes_t attr;
/*
@ -81,32 +83,38 @@ typedef struct {
* PSA_SLOT_FULL. */
psa_key_slot_state_t state;
/*
* Number of functions registered as reading the material in the key slot.
*
* Library functions must not write directly to registered_readers
*
* A function must call psa_register_read(slot) before reading the current
* contents of the slot for an operation.
* They then must call psa_unregister_read(slot) once they have finished
* reading the current contents of the slot. If the key slot mutex is not
* held (when mutexes are enabled), this call must be done via a call to
* psa_unregister_read_under_mutex(slot).
* A function must call psa_key_slot_has_readers(slot) to check if
* the slot is in use for reading.
*
* This counter is used to prevent resetting the key slot while the library
* may access it. For example, such control is needed in the following
* scenarios:
* . In case of key slot starvation, all key slots contain the description
* of a key, and the library asks for the description of a persistent
* key not present in the key slots, the key slots currently accessed by
* the library cannot be reclaimed to free a key slot to load the
* persistent key.
* . In case of a multi-threaded application where one thread asks to close
* or purge or destroy a key while it is in use by the library through
* another thread. */
size_t registered_readers;
union {
struct {
/*
* Number of functions registered as reading the material in the key slot.
*
* Library functions must not write directly to registered_readers
*
* A function must call psa_register_read(slot) before reading
* the current contents of the slot for an operation.
* They then must call psa_unregister_read(slot) once they have
* finished reading the current contents of the slot. If the key
* slot mutex is not held (when mutexes are enabled), this call
* must be done via a call to
* psa_unregister_read_under_mutex(slot).
* A function must call psa_key_slot_has_readers(slot) to check if
* the slot is in use for reading.
*
* This counter is used to prevent resetting the key slot while
* the library may access it. For example, such control is needed
* in the following scenarios:
* . In case of key slot starvation, all key slots contain the
* description of a key, and the library asks for the
* description of a persistent key not present in the
* key slots, the key slots currently accessed by the
* library cannot be reclaimed to free a key slot to load
* the persistent key.
* . In case of a multi-threaded application where one thread
* asks to close or purge or destroy a key while it is in use
* by the library through another thread. */
size_t registered_readers;
} occupied;
} var;
/* Dynamically allocated key data buffer.
* Format as specified in psa_export_key(). */
@ -169,7 +177,7 @@ typedef struct {
*/
static inline int psa_key_slot_has_readers(const psa_key_slot_t *slot)
{
return slot->registered_readers > 0;
return slot->var.occupied.registered_readers > 0;
}
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)

View File

@ -268,7 +268,7 @@ void psa_wipe_all_key_slots(void)
for (size_t slice_idx = 0; slice_idx < KEY_SLICE_COUNT; slice_idx++) {
for (size_t slot_idx = 0; slot_idx < key_slice_length(slice_idx); slot_idx++) {
psa_key_slot_t *slot = get_key_slot(slice_idx, slot_idx);
slot->registered_readers = 1;
slot->var.occupied.registered_readers = 1;
slot->state = PSA_SLOT_PENDING_DELETION;
(void) psa_wipe_key_slot(slot);
}
@ -568,12 +568,12 @@ psa_status_t psa_unregister_read(psa_key_slot_t *slot)
/* If we are the last reader and the slot is marked for deletion,
* we must wipe the slot here. */
if ((slot->state == PSA_SLOT_PENDING_DELETION) &&
(slot->registered_readers == 1)) {
(slot->var.occupied.registered_readers == 1)) {
return psa_wipe_key_slot(slot);
}
if (psa_key_slot_has_readers(slot)) {
slot->registered_readers--;
slot->var.occupied.registered_readers--;
return PSA_SUCCESS;
}
@ -707,7 +707,7 @@ psa_status_t psa_close_key(psa_key_handle_t handle)
return status;
}
if (slot->registered_readers == 1) {
if (slot->var.occupied.registered_readers == 1) {
status = psa_wipe_key_slot(slot);
} else {
status = psa_unregister_read(slot);
@ -742,7 +742,7 @@ psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
}
if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
(slot->registered_readers == 1)) {
(slot->var.occupied.registered_readers == 1)) {
status = psa_wipe_key_slot(slot);
} else {
status = psa_unregister_read(slot);

View File

@ -174,10 +174,10 @@ static inline psa_status_t psa_key_slot_state_transition(
static inline psa_status_t psa_register_read(psa_key_slot_t *slot)
{
if ((slot->state != PSA_SLOT_FULL) ||
(slot->registered_readers >= SIZE_MAX)) {
(slot->var.occupied.registered_readers >= SIZE_MAX)) {
return PSA_ERROR_CORRUPTION_DETECTED;
}
slot->registered_readers++;
slot->var.occupied.registered_readers++;
return PSA_SUCCESS;
}