From 091b4eabbec574bd5ad706ed24ccdf1ed8dddd5e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 5 Jun 2026 19:41:59 +0200 Subject: [PATCH] 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. --- src/tpm.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/tpm.c b/src/tpm.c index 98ed7f60..71ec29d9 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -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 +#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);