F-4644: validate NS pointers in TPM CSME_NSE_API veneers with cmse_check_address_range

The eleven CSME_NSE_API TPM veneers in src/tpm.c are cmse_nonsecure_entry
gateways when built with TZEN=1 (e.g. config/examples/stm32h5-tz-tpm.config,
which exposes them to the non-secure STM32H5 test app). Each accepted a typed
pointer from the non-secure caller and immediately used it as a dereference or
memset/XMEMSET target on the Secure side -- memset(caps), memset(handles),
memset(getTime), XMEMSET(quoteResult), and the in/out forwards to
TPM2_GetCapability / TPM2_ParseAttest. Because Secure code can write Secure
SRAM, a malicious non-secure caller could pass a Secure pointer and turn any of
these veneers into a confused-deputy write primitive against Secure memory.

Validate every non-secure-supplied pointer with cmse_check_address_range
(CMSE_NONSECURE, plus CMSE_MPU_READWRITE for write targets) before the first
use, returning BAD_FUNC_ARG (or NULL for the string helpers) when the range is
not accessible from the non-secure world. The check is wrapped in
WOLFBOOT_TPM_NS_RW/WOLFBOOT_TPM_NS_R, guarded by __ARM_FEATURE_CMSE == 3U so
non-CMSE builds (where there is no security boundary) collapse to a plain
non-NULL pass-through. Buffer sizes use the caller-provided/known capacities
(name_sz, error_sz, *certSz, PCR digest size).

Verified by building wolfboot.elf with the stm32h5-tz-tpm config (-mcmse);
the bug itself is a TrustZone Secure/Non-secure partitioning issue that cannot
be exercised on the host unit-test build.
pull/788/head
Daniele Lacamera 2026-06-05 19:41:59 +02:00
parent d175c819cd
commit 091b4eabbe
1 changed files with 66 additions and 0 deletions

View File

@ -1230,14 +1230,39 @@ static int wolfRNG_GetSeedCB(OS_Seed* os, uint8_t* seed, uint32_t sz)
/* API's that are callable from non-secure code */
/* Validate that a buffer supplied by the non-secure caller is fully
* accessible from the non-secure world before the secure side dereferences
* it. Without this check a non-secure caller could pass a pointer into Secure
* SRAM and turn these veneers into a confused-deputy write primitive against
* Secure memory. Outside of a CMSE secure build there is no security boundary,
* so the checks collapse to a simple non-NULL pass-through. */
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
#include <arm_cmse.h>
#define WOLFBOOT_TPM_NS_RW(p, sz) \
cmse_check_address_range((void*)(p), (size_t)(sz), \
CMSE_NONSECURE | CMSE_MPU_READWRITE)
#define WOLFBOOT_TPM_NS_R(p, sz) \
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
#else
#define WOLFBOOT_TPM_NS_RW(p, sz) ((void*)(p))
#define WOLFBOOT_TPM_NS_R(p, sz) ((void*)(p))
#endif
int CSME_NSE_API wolfBoot_tpm2_caps(WOLFTPM2_CAPS* caps)
{
if (WOLFBOOT_TPM_NS_RW(caps, sizeof(*caps)) == NULL) {
return BAD_FUNC_ARG;
}
memset(caps, 0, sizeof(*caps));
return wolfTPM2_GetCapabilities(&wolftpm_dev, caps);
}
int CSME_NSE_API wolfBoot_tpm2_get_handles(TPM_HANDLE handle, TPML_HANDLE* handles)
{
if (WOLFBOOT_TPM_NS_RW(handles, sizeof(*handles)) == NULL) {
return BAD_FUNC_ARG;
}
memset(handles, 0, sizeof(*handles));
return wolfTPM2_GetHandles(handle, handles);
}
@ -1249,6 +1274,9 @@ const char* CSME_NSE_API wolfBoot_tpm2_get_alg_name(TPM_ALG_ID alg,
if (name == NULL || name_sz <= 0) {
return NULL;
}
if (WOLFBOOT_TPM_NS_RW(name, name_sz) == NULL) {
return NULL;
}
s_name = TPM2_GetAlgName(alg);
if (s_name != NULL && name != NULL && name_sz > 0) {
strncpy(name, s_name, name_sz - 1);
@ -1267,6 +1295,9 @@ const char* CSME_NSE_API wolfBoot_tpm2_get_rc_string(int rc, char* error, int er
if (error == NULL || error_sz <= 0) {
return NULL;
}
if (WOLFBOOT_TPM_NS_RW(error, error_sz) == NULL) {
return NULL;
}
s_error = TPM2_GetRCString(rc);
if (s_error != NULL && error != NULL && error_sz > 0) {
strncpy(error, s_error, error_sz - 1);
@ -1281,17 +1312,32 @@ const char* CSME_NSE_API wolfBoot_tpm2_get_rc_string(int rc, char* error, int er
int CSME_NSE_API wolfBoot_tpm2_get_capability(GetCapability_In* in, GetCapability_Out* out)
{
if (WOLFBOOT_TPM_NS_R(in, sizeof(*in)) == NULL ||
WOLFBOOT_TPM_NS_RW(out, sizeof(*out)) == NULL) {
return BAD_FUNC_ARG;
}
return (int)TPM2_GetCapability(in, out);
}
int CSME_NSE_API wolfBoot_tpm2_read_pcr(uint8_t pcrIndex, uint8_t* digest, int* digestSz)
{
if (WOLFBOOT_TPM_NS_RW(digest,
TPM2_GetHashDigestSize(WOLFBOOT_TPM_PCR_ALG)) == NULL ||
WOLFBOOT_TPM_NS_RW(digestSz, sizeof(*digestSz)) == NULL) {
return BAD_FUNC_ARG;
}
return wolfTPM2_ReadPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG,
digest, digestSz);
}
int CSME_NSE_API wolfBoot_tpm2_read_cert(uint32_t handle, uint8_t* cert, uint32_t* certSz)
{
if (WOLFBOOT_TPM_NS_RW(certSz, sizeof(*certSz)) == NULL) {
return BAD_FUNC_ARG;
}
if (WOLFBOOT_TPM_NS_RW(cert, *certSz) == NULL) {
return BAD_FUNC_ARG;
}
wolfTPM2_SetAuthPassword(&wolftpm_dev, 0, NULL);
return wolfTPM2_NVReadCert(&wolftpm_dev, handle, cert, certSz);
}
@ -1304,6 +1350,13 @@ int CSME_NSE_API wolfBoot_tpm2_get_aik(WOLFTPM2_KEY* aik,
if (aik == NULL) {
return BAD_FUNC_ARG;
}
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL) {
return BAD_FUNC_ARG;
}
if (masterPassword != NULL &&
WOLFBOOT_TPM_NS_R(masterPassword, masterPasswordSz) == NULL) {
return BAD_FUNC_ARG;
}
/* Load existing AIK and set auth */
rc = wolfTPM2_ReadPublicKey(&wolftpm_dev, aik, TPM2_IAK_KEY_HANDLE);
@ -1330,6 +1383,10 @@ int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* get
if (aik == NULL || getTime == NULL) {
return BAD_FUNC_ARG;
}
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL ||
WOLFBOOT_TPM_NS_RW(getTime, sizeof(*getTime)) == NULL) {
return BAD_FUNC_ARG;
}
memset(getTime, 0, sizeof(*getTime));
memset(&eh_handle, 0, sizeof(eh_handle));
@ -1358,6 +1415,10 @@ int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* get
int CSME_NSE_API wolfBoot_tpm2_parse_attest(const TPM2B_ATTEST* in, TPMS_ATTEST* out)
{
if (WOLFBOOT_TPM_NS_R(in, sizeof(*in)) == NULL ||
WOLFBOOT_TPM_NS_RW(out, sizeof(*out)) == NULL) {
return BAD_FUNC_ARG;
}
return TPM2_ParseAttest(in, out);
}
@ -1372,6 +1433,11 @@ int CSME_NSE_API wolfBoot_tpm2_quote(WOLFTPM2_KEY* aik,
quoteResult == NULL) {
return BAD_FUNC_ARG;
}
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL ||
WOLFBOOT_TPM_NS_R(pcrArray, pcrArraySz) == NULL ||
WOLFBOOT_TPM_NS_RW(quoteResult, sizeof(*quoteResult)) == NULL) {
return BAD_FUNC_ARG;
}
/* set auth for using the AIK */
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &aik->handle);