F-5672: cache NS in_vec[1].len in ARM_TEE_PS_SET to close TOCTOU double-fetch

The ARM_TEE_PS_SET branch of arm_tee_psa_call() read in_vec[1].len
directly from non-secure memory four times: the WOLFBOOT_PS_MAX_DATA
bounds check, the null-data guard, the copy gate, the XMEMCPY length,
and the entry->size assignment. in_vec lives in NS RAM, and on ARMv8-M
with AIRCR.PRIS=0 a higher-priority NS interrupt can preempt Secure
thread-mode execution (and an NS-accessible DMA engine can mutate NS RAM
independently) between the bounds check and the copy. An NS attacker
racing either mechanism could grow the length past WOLFBOOT_PS_MAX_DATA
after it was validated, overflowing the fixed entry->data[512] buffer in
Secure SRAM into adjacent Secure globals.

Snapshot the length once into a Secure-stack local (data_len) right
after capturing the in_vec bases and use only that local for every
subsequent check, the XMEMCPY operand, and entry->size. This follows the
standard PSA/CMSE practice of copying NS-supplied scalars to the Secure
stack before validating and using them.
pull/788/head
Daniele Lacamera 2026-06-05 18:50:47 +02:00
parent e27e81f659
commit 8cff2e8792
1 changed files with 12 additions and 5 deletions

View File

@ -766,16 +766,23 @@ int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type,
const void *data;
const psa_storage_create_flags_t *flags;
struct wolfboot_ps_entry *entry;
size_t data_len;
if (in_vec == NULL || in_len < 3) {
return PSA_ERROR_INVALID_ARGUMENT;
}
uid = (const psa_storage_uid_t *)in_vec[0].base;
data = in_vec[1].base;
flags = (const psa_storage_create_flags_t *)in_vec[2].base;
/* Snapshot the NS-supplied length once into a Secure-stack local.
* in_vec lives in NS memory and may be mutated concurrently (a
* preempting NS interrupt or NS-accessible DMA), so re-reading
* in_vec[1].len after the bounds check would allow a TOCTOU
* double-fetch to grow the copy past WOLFBOOT_PS_MAX_DATA. */
data_len = in_vec[1].len;
if (uid == NULL || flags == NULL) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (in_vec[1].len > WOLFBOOT_PS_MAX_DATA) {
if (data_len > WOLFBOOT_PS_MAX_DATA) {
return PSA_ERROR_INSUFFICIENT_STORAGE;
}
entry = wolfboot_ps_find(*uid);
@ -787,13 +794,13 @@ int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type,
} else if ((entry->flags & PSA_STORAGE_FLAG_WRITE_ONCE) != 0U) {
return PSA_ERROR_NOT_PERMITTED;
}
if (in_vec[1].len > 0 && data == NULL) {
if (data_len > 0 && data == NULL) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (in_vec[1].len > 0) {
XMEMCPY(entry->data, data, in_vec[1].len);
if (data_len > 0) {
XMEMCPY(entry->data, data, data_len);
}
entry->size = in_vec[1].len;
entry->size = data_len;
entry->flags = *flags;
return PSA_SUCCESS;
}