Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ tools/unit-tests/unit-max-space
tools/unit-tests/unit-sdhci-disk-unaligned
tools/unit-tests/unit-fwtpm-stub
tools/unit-tests/unit-gzip
tools/unit-tests/unit-linux-loader-e820
tools/unit-tests/unit-linux-loader-syssize


# Elf preprocessing tools
Expand Down
17 changes: 12 additions & 5 deletions src/arm_tee_psa_ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down
31 changes: 25 additions & 6 deletions src/fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ static uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
{
const uint32_t *tagp, *lenp;
uint32_t tag;
uint32_t proplen;
uint64_t next_off;
int offset = startoffset;
const char *p;

Expand Down Expand Up @@ -152,13 +154,30 @@ static uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
if (!lenp) {
return FDT_END; /* premature end */
}
/* skip-name offset, length and value */
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ fdt32_to_cpu(*lenp);
if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
((offset - fdt32_to_cpu(*lenp)) % 8) != 0) {
offset += 4;
proplen = fdt32_to_cpu(*lenp);
/* A property value can never be larger than the blob itself.
* Reject an oversized length up front: otherwise the unsigned
* cursor arithmetic below wraps (e.g. len=0xFFFFFFFF advances
* offset by only 7 bytes), the malformed node slips past the
* fdt_offset_ptr() bounds check, and the bogus length propagates
* to callers as a negative int (a ~4GB memcpy size). */
if (proplen > (uint32_t)fdt_totalsize(fdt)) {
return FDT_END; /* bad structure */
}
/* skip-name offset, length and value. Accumulate the next cursor in a
* 64-bit unsigned so neither the addition nor the narrowing back to the
* signed int offset can overflow, then re-validate it against the blob
* size before continuing. */
next_off = (uint64_t)offset
+ (sizeof(struct fdt_property) - FDT_TAGSIZE) + proplen;
if (fdt_version(fdt) < 0x10 && proplen >= 8 &&
((next_off - proplen) % 8) != 0) {
next_off += 4;
}
if (next_off > (uint64_t)fdt_totalsize(fdt)) {
return FDT_END; /* bad structure */
}
offset = (int)next_off;
break;

case FDT_END:
Expand Down
29 changes: 29 additions & 0 deletions src/fwtpm_callable.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@
#include "wolftpm/fwtpm/fwtpm_nv.h"
#include "wolftpm/tpm2_types.h"

/* Validate that a buffer supplied by the non-secure caller lives in 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 this veneer
* into a confused-deputy primitive (forging TPM responses into Secure memory or
* leaking Secure memory through the command path). The relevant property is the
* Secure/Non-secure attribution (SAU/IDAU): only CMSE_NONSECURE is checked, not
* the MPU read/write permission bits. The MPU bits depend on an enabled NS MPU
* (they read back as 0 when NO_MPU is set) and do not constrain Secure accesses
* to NS memory anyway, so requiring them would wrongly reject valid NS buffers.
* 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 WCS_FWTPM_NS_RW(p, sz) \
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
#define WCS_FWTPM_NS_R(p, sz) \
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
#else
#define WCS_FWTPM_NS_RW(p, sz) ((void*)(p))
#define WCS_FWTPM_NS_R(p, sz) ((void*)(p))
#endif

static FWTPM_CTX fwtpm_ctx;
static int fwtpm_ready;

Expand Down Expand Up @@ -129,11 +151,18 @@ int CSME_NSE_API wcs_fwtpm_transmit(const uint8_t *cmd, uint32_t cmdSz,
cmdSz > WCS_FWTPM_MAX_COMMAND_SIZE) {
return BAD_FUNC_ARG;
}
if (WCS_FWTPM_NS_R(cmd, cmdSz) == NULL ||
WCS_FWTPM_NS_RW(rspSz, sizeof(*rspSz)) == NULL) {
return BAD_FUNC_ARG;
}

rspCapacity = *rspSz;
if (rspCapacity == 0U || rspCapacity > WCS_FWTPM_MAX_COMMAND_SIZE) {
return BAD_FUNC_ARG;
}
if (WCS_FWTPM_NS_RW(rsp, rspCapacity) == NULL) {
return BAD_FUNC_ARG;
}

rspLen = (int)rspCapacity;
rc = FWTPM_ProcessCommand(&fwtpm_ctx, cmd, (int)cmdSz, rsp, &rspLen, 0);
Expand Down
21 changes: 21 additions & 0 deletions src/libwolfboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -2419,6 +2419,25 @@ int wolfBoot_ram_decrypt(uint8_t *src, uint8_t *dst)
#endif /* EXT_ENCRYPTED */

#if defined(__WOLFBOOT) && defined(TZEN)

/* The wolfBoot_nsc_* functions are cmse_nonsecure_entry veneers callable by any
* non-secure code. Pointer arguments arrive directly from the NS caller and are
* used as the write target of the secure callee. Validate the whole range lives
* in the non-secure world before writing, otherwise an NS caller could aim the
* write at Secure SRAM (a confused-deputy write primitive). The check verifies
* only the Secure/Non-secure attribution (CMSE_NONSECURE); the MPU read/write
* permission bits are deliberately not required, as they read back as 0 when the
* NS MPU is disabled (NO_MPU) and do not constrain Secure accesses to NS memory
* anyway. Outside a CMSE secure build there is no security boundary, so the check
* collapses to a non-NULL pass-through. Same fix pattern as F-4416/F-4417/F-4644. */
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
#include <arm_cmse.h>
#define WOLFBOOT_NSC_NS_RW(p, sz) \
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
#else
#define WOLFBOOT_NSC_NS_RW(p, sz) ((void*)(p))
#endif

CSME_NSE_API
void wolfBoot_nsc_success(void)
{
Expand All @@ -2440,6 +2459,8 @@ uint32_t wolfBoot_nsc_get_image_version(uint8_t part)
CSME_NSE_API
int wolfBoot_nsc_get_partition_state(uint8_t part, uint8_t *st)
{
if (WOLFBOOT_NSC_NS_RW(st, sizeof(uint8_t)) == NULL)
return -1;
return wolfBoot_get_partition_state(part, st);
}

Expand Down
9 changes: 9 additions & 0 deletions src/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,15 @@ static int pci_program_bar(uint8_t bus, uint8_t dev, uint8_t fun,
(is_mmio ? "mm" : "io"), bus, dev, fun, bar_idx,
(uint32_t)bar_value, *is_64bit ? "64bit" : "",
is_prefetch ? "prefetch" : "");
/* A BAR with no writable address bits (bar_align == 0) is unimplemented
* or malformed: (~0) + 1 would wrap to length 0, leaving the allocator
* cursor unchanged and colliding the next BAR onto the same address.
* Treat it as unimplemented and skip it. Legitimate MMIO BARs always
* have at least one writable address bit; IO BARs force the high bits
* above, so bar_align is never 0 for them. */
if (bar_align == 0)
goto restore_bar;

align = length = (~bar_align) + 1;
/* force pci address to be on page boundary */
if (align < 0x1000)
Expand Down
6 changes: 6 additions & 0 deletions src/pkcs11_store.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ static void bitmap_put(uint32_t pos, int val)
uint32_t bit = pos % 8;
uint8_t *bitmap = cached_sector + sizeof(uint32_t);

/* Reject out-of-range positions (e.g. a power-fault-corrupted hdr->pos
* left as erased flash) to avoid an out-of-bounds write past the
* bitmap, which lives within cached_sector. */
if (pos >= KEYVAULT_MAX_ITEMS)
return;

if (val != 0) {
bitmap[octet] |= (1 << bit);
} else {
Expand Down
68 changes: 68 additions & 0 deletions src/tpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1230,14 +1230,41 @@ 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 lives in 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. The check
* verifies only the Secure/Non-secure attribution (CMSE_NONSECURE); the MPU
* read/write permission bits are deliberately not required, as they read back as
* 0 when the NS MPU is disabled (NO_MPU) and do not constrain Secure accesses to
* NS memory anyway. 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)
#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);
}
Expand All @@ -1249,6 +1276,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);
Expand All @@ -1267,6 +1297,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);
Expand All @@ -1281,17 +1314,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);
}
Expand All @@ -1304,6 +1352,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);
Expand All @@ -1330,6 +1385,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));
Expand Down Expand Up @@ -1358,6 +1417,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);
}

Expand All @@ -1372,6 +1435,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);
Expand Down
21 changes: 21 additions & 0 deletions src/wc_callable.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,31 @@
#include "wolfboot/wcs_fwtpm.h"
#endif

/* wcs_get_random is a cmse_nonsecure_entry veneer: the rand pointer and size
* arrive from the non-secure caller and are used as the write target of
* wc_RNG_GenerateBlock. Validate the whole range lives in the non-secure world
* before writing, otherwise an NS caller could aim the RNG output at Secure SRAM
* (a confused-deputy write primitive). The check verifies only the
* Secure/Non-secure attribution (CMSE_NONSECURE); the MPU read/write permission
* bits are deliberately not required, as they read back as 0 when the NS MPU is
* disabled (NO_MPU) and do not constrain Secure accesses to NS memory anyway.
* Outside a CMSE secure build there is no security boundary, so the check
* collapses to a non-NULL pass-through. */
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
#include <arm_cmse.h>
#define WOLFBOOT_WCS_NS_RW(p, sz) \
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
#else
#define WOLFBOOT_WCS_NS_RW(p, sz) ((void*)(p))
#endif

static WC_RNG wcs_rng;

int CSME_NSE_API wcs_get_random(uint8_t *rand, uint32_t size)
{
if (WOLFBOOT_WCS_NS_RW(rand, size) == NULL) {
return BAD_FUNC_ARG;
}
return wc_RNG_GenerateBlock(&wcs_rng, rand, size);
}

Expand Down
Loading
Loading