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
43 changes: 43 additions & 0 deletions docs/Security-Model.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,47 @@ moved onto a seal while another context runs, so this check does not replace
the integrity signature the processor places in, and checks on, the Secure
frames it stacks itself.

### SPM stack limit and fault attribution

The Secure main stack has a fixed size (`WT_SPM_STACK_SIZE`, 16 KiB), reserved
by the linker below `_estack`; the link fails if `.bss` reaches it. Reset sets
`MSPLIM_S` to its bottom right after MSP is placed under the seal words, and
boot refuses to continue unless the limit reads back. An SPM stack overflow
raises `UsageFault.STKOF` on the main stack; the handler halts the platform on
the production panic without touching the stack, and a real `HardFault`
handler does the same for any escalated fault, latching `CFSR`, `HFSR`,
`MMFAR`, `BFAR`, the stacked PC and `EXC_RETURN` for a debugger instead of
spinning silently.

`BusFault` is enabled alongside `MemManage` and `UsageFault`. All three take one
dispatcher, which attributes the fault by the frame it finds: a Secure Thread
frame on the process stack with a scheduled partition or wolfHSM tasklet
current is that partition's fault (precise bus errors carry `BFAR`; an
imprecise one is drained by the barrier every context switch issues, so it is
still pending against the partition that issued the write), and the partition
restarts under its manifest policy. A frame from a privileged handler, the
bootstrap thread, or no current coroutine is the SPM's own fault and halts the
platform. A Non-secure bus error targets the Secure `BusFault` as well
(`BFHFNMINS` is 0); it is routed to the guest fault path and restarts the
guest. A partition that issues the scheduler's internal guest-return `SVC` is
resumed on the PROGRAMMER ERROR trap and restarts alone.

### Execute-never Secure RAM

The SPM whitelist maps every writable Secure RAM region execute-never. The one
executable Secure RAM window is the MIMXRT700's RAMFUNC band, which holds the
NSC gateway and NOR routines and is mapped read-only. While an
unprivileged partition thread runs, its MPU table keeps `PRIVDEFENA` so the
privileged SVC gate and its deputies can reach SPM state, and the default map
would let privileged code execute from SRAM; one extra region therefore covers
the SPM's own RAM (the boot-handoff scratch, `.data`, `.bss`, the main stack)
privileged-only and execute-never on every partition dispatch, and a domain
region inside that window fails the dispatch closed. A production partition
table must leave the region free; the Arm conformance client partition fills
an 8-region MPU with its window grants, and the conformance image counts those
dispatches in `g_wt_xn_denied` instead (the STM32H563 implements 12 regions,
so it is covered there).

### Link-time optimization

The Secure image enables GCC link-time optimization by default. LTO can replace
Expand Down Expand Up @@ -270,6 +311,8 @@ engine images.
- [FF-M gateway](../src/arch/armv8m/ffm_nsc.c)
- [Secure Partition scheduler and SVC gates](../src/arch/armv8m/spm_svc.c)
- [Secure stack sealing and context switch](../src/arch/armv8m/coroutine_armv8m.c)
- [Secure fault attribution and SPM halt](../src/arch/armv8m/sp_fault_armv8m.c)
- [Secure MPU tables and the SPM RAM cover](../src/arch/armv8m/mpu_armv8m.c)
- [Guest verification](../src/guest_verify.c)
- [HSM relay binding](../src/services/wolfhsm/wt_hsm.c)
- [Native crypto dispatch](../src/services/native/crypto_native.c)
Expand Down
23 changes: 23 additions & 0 deletions docs/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,29 @@ than their names suggest:
parked on its stack top, so the exception frame lands on the seal words;
that partition alone faults and restarts, and the guests keep running.

Three more cover the SPM's own fault handling:

- `mspovfneg` pushes on the Secure main stack in the reset path until
`MSPLIM_S` raises STKOF. M33MU escalates the entry-time STKOF to HardFault
and ends the run there without executing the handler, so the emulator
proves only the limit; the STM32H563 run reads the SPM fault latch and
checks that no guest ran.
- `xnneg` makes the privileged SVC gate call a thunk copied into SPM `.bss`
while a partition thread domain is installed; the execute-never cover faults
the fetch. M33MU pends that synchronous fault instead of escalating it past
the active SVC, so the emulator proves only the denied fetch; the STM32H563
run checks the SPM fault latch and the halt.
- `svcneg` has the ITS partition issue the scheduler's internal guest-return
SVC; the partition alone is panicked and restarted, and the lifecycle
completes.

`busfaultneg` (the SERVICE_HSM partition reads an MPU-permitted window past
the end of physical SRAM) and `nsbusfaultneg` (guest0 turns off its own MPU and
reads an unmapped Non-secure peripheral hole; the monitor restarts it to its
limit while guest1 runs) run only on the STM32H563: M33MU turns an unmapped
data access into a MemManage fault and never vectors a data BusFault, so these
scenarios have no emulator row until the pinned emulator models it.

VNET has convenience targets:

```sh
Expand Down
4 changes: 4 additions & 0 deletions include/wolftrust/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ uintptr_t wt_arch_sp_stack_pointer(void);
void wt_arch_sp_redirect_to_panic_trap(wt_trap_frame_t* frame);
void wt_arch_assert_privileged_thread(void);
void wt_arch_sp_fault_probe(unsigned int code);
#if defined(WT_SVC_NEG_PROBE) && (WT_SVC_NEG_PROBE == 1)
/* svcneg: issue the scheduler's internal guest-return trap from a partition. */
void wt_arch_sp_guest_return_probe(void);
#endif
void wt_arch_diag_trap(uint32_t a, uint32_t b, uint32_t c);
void wt_arch_sp_panic(uint32_t op, uint32_t code, uint32_t extra)
__attribute__((noreturn));
Expand Down
18 changes: 18 additions & 0 deletions include/wolftrust/arch/armv8m/core_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#define WT_SCB_SHPR3_S (*(volatile uint32_t*)0xE000ED20u)
#define WT_SCB_SHCSR_S (*(volatile uint32_t*)0xE000ED24u)
#define WT_SCB_CFSR_S (*(volatile uint32_t*)0xE000ED28u)
#define WT_SCB_HFSR_S (*(volatile uint32_t*)0xE000ED2Cu)
#define WT_SCB_MMFAR_S (*(volatile uint32_t*)0xE000ED34u)
#define WT_SCB_BFAR_S (*(volatile uint32_t*)0xE000ED38u)
#define WT_SCB_ICSR_S (*(volatile uint32_t*)0xE000ED04u)
Expand All @@ -42,6 +43,7 @@
#define WT_SCB_AIRCR_VECTKEY (0x05FAu << 16)
#define WT_SCB_AIRCR_SYSRESETREQ (WT_SCB_AIRCR_VECTKEY | (1u << 2))
#define WT_SCB_AIRCR_SYSRESETREQS (1u << 3)
#define WT_SCB_AIRCR_BFHFNMINS (1u << 13)
/* Config bits that must be preserved across an AIRCR read-modify-write. */
#define WT_SCB_AIRCR_CFG_MASK ((1u << 3) | (1u << 13) | (1u << 14) | (7u << 8))
#define WT_SCB_ICSR_PENDSVCLR (1u << 27)
Expand All @@ -67,7 +69,23 @@
#define WT_SCB_CFSR_MMFSR_MLSPERR (1u << 5)
#define WT_SCB_CFSR_MMFSR_MMARVALID (1u << 7)

#define WT_SCB_CFSR_BFSR_IBUSERR (1u << 8)
#define WT_SCB_CFSR_BFSR_PRECISERR (1u << 9)
#define WT_SCB_CFSR_BFSR_IMPRECISERR (1u << 10)
#define WT_SCB_CFSR_BFSR_UNSTKERR (1u << 11)
#define WT_SCB_CFSR_BFSR_STKERR (1u << 12)
#define WT_SCB_CFSR_BFSR_LSPERR (1u << 13)
#define WT_SCB_CFSR_BFSR_BFARVALID (1u << 15)

#define WT_SCB_HFSR_FORCED (1u << 30)

#define WT_SCB_CFSR_UFSR_STKOF (1u << 20) /* UFSR bit 4 lifted to CFSR bit 20 */
/* Faults that leave no readable exception frame at the faulting SP. */
#define WT_SCB_CFSR_NO_FRAME (WT_SCB_CFSR_MMFSR_MUNSTKERR | \
WT_SCB_CFSR_MMFSR_MSTKERR | \
WT_SCB_CFSR_BFSR_UNSTKERR | \
WT_SCB_CFSR_BFSR_STKERR | \
WT_SCB_CFSR_UFSR_STKOF)
#define WT_SCB_CFSR_UFSR_NOCP (1u << 19) /* UFSR bit 3: coprocessor disabled */
#define WT_SCB_CFSR_UFSR_UNDEFINSTR (1u << 16) /* UFSR bit 0: undefined instruction */

Expand Down
4 changes: 4 additions & 0 deletions include/wolftrust/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ typedef struct wt_trap_frame wt_trap_frame_t;
#include "wolftrust/types.h"

void wt_platform_init(void);
#if defined(WT_BUSFAULT_NEG_PROBE) && (WT_BUSFAULT_NEG_PROBE == 1)
/* busfaultneg: a readable region whose bus returns an error on this SoC. */
void wt_platform_busfault_probe_region(wt_memory_region_t* region);
#endif
/* Only asked of ports providing WT_PORT_CAPABILITY_TZ_FILTER. */
void wt_platform_program_memory_windows(const wt_memory_window_t* windows,
size_t count);
Expand Down
11 changes: 10 additions & 1 deletion mk/arch-armv8m.mk
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ ARCH_CFLAGS := -mcmse -DWT_TARGET_BUILD=1
# build must carry exactly its one deliberate FP instruction and no other.
WT_FP_NEG_PROBE ?= 0
WT_SEAL_NEG_PROBE ?= 0
WT_MSP_OVF_PROBE ?= 0
WT_XN_NEG_PROBE ?= 0
ARCH_FP_SCAN_FLAGS :=
ifeq ($(WT_FP_NEG_PROBE),1)
ARCH_CFLAGS += -DWT_FP_NEG_PROBE=1
Expand All @@ -16,6 +18,12 @@ endif
ifneq ($(filter 1 2 3 4,$(WT_SEAL_NEG_PROBE)),)
ARCH_CFLAGS += -DWT_SEAL_NEG_PROBE=$(WT_SEAL_NEG_PROBE)
endif
ifeq ($(WT_MSP_OVF_PROBE),1)
ARCH_CFLAGS += -DWT_MSP_OVF_PROBE=1
endif
ifeq ($(WT_XN_NEG_PROBE),1)
ARCH_CFLAGS += -DWT_XN_NEG_PROBE=1
endif
WT_WOLFCRYPT_SP_ASM ?= 1
WT_WOLFCRYPT_ARMASM ?= 1

Expand Down Expand Up @@ -96,6 +104,7 @@ define arch_image_checks
@$(TOOLPREFIX)size -A -x $(SECURE_ELF) > $(BUILD_DIR)/sec-sections.txt || \
{ echo "FAIL: size on the secure image failed" >&2; exit 1; }
@estack=$$(awk '$$3 == "_estack" { print $$1 }' $(BUILD_DIR)/nsc-syms.txt); \
sstack=$$(awk '$$3 == "_sstack" { print $$1 }' $(BUILD_DIR)/nsc-syms.txt); \
python3 $(ROOT)/tools/check_stack_seal.py --estack "$$estack" \
$(BUILD_DIR)/sec-sections.txt
--sstack "$$sstack" $(BUILD_DIR)/sec-sections.txt
endef
16 changes: 16 additions & 0 deletions mk/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ WT_MAX_GUESTS ?= 2
# the SP_SMALL math switch; PSPLIM_S faults any real overflow, so this floor
# is measured, not guessed.
WT_CO_STACK_SIZE ?= 10240
# Secure main (SPM) stack, sized at link and limited by MSPLIM_S at reset.
WT_SPM_STACK_SIZE ?= 0x4000
WT_LTO ?= 1
ifneq ($(WT_LTO),0)
ifneq ($(WT_LTO),1)
Expand Down Expand Up @@ -84,6 +86,8 @@ WT_SP_FAULT_PROBE ?= 0
WT_HSM_FAULT_PROBE ?= 0
WT_SP_FAULT_ALWAYS_PROBE ?= 0
WT_PANIC_NEG_PROBE ?= 0
WT_BUSFAULT_NEG_PROBE ?= 0
WT_SVC_NEG_PROBE ?= 0
WT_VNET_NEG_PROBE ?= 0
WT_MANIFEST_NEG_PROBE ?= 0
WT_REMEASURE_PROBE ?= 0
Expand Down Expand Up @@ -192,6 +196,12 @@ endif
ifeq ($(WT_PANIC_NEG_PROBE),1)
SECURE_CFLAGS += -DWT_PANIC_NEG_PROBE=1
endif
ifeq ($(WT_BUSFAULT_NEG_PROBE),1)
SECURE_CFLAGS += -DWT_BUSFAULT_NEG_PROBE=1
endif
ifeq ($(WT_SVC_NEG_PROBE),1)
SECURE_CFLAGS += -DWT_SVC_NEG_PROBE=1
endif
ifeq ($(WT_VNET_NEG_PROBE),1)
SECURE_CFLAGS += -DWT_VNET_NEG_PROBE=1
endif
Expand Down Expand Up @@ -1428,10 +1438,15 @@ $(BUILD_MODE_STAMP): FORCE | $(BUILD_DIR)
'WT_MANIFEST_NEG_PROBE=$(WT_MANIFEST_NEG_PROBE)' \
'WT_FP_NEG_PROBE=$(WT_FP_NEG_PROBE)' \
'WT_SEAL_NEG_PROBE=$(WT_SEAL_NEG_PROBE)' \
'WT_MSP_OVF_PROBE=$(WT_MSP_OVF_PROBE)' \
'WT_BUSFAULT_NEG_PROBE=$(WT_BUSFAULT_NEG_PROBE)' \
'WT_XN_NEG_PROBE=$(WT_XN_NEG_PROBE)' \
'WT_SVC_NEG_PROBE=$(WT_SVC_NEG_PROBE)' \
'WT_REMEASURE_PROBE=$(WT_REMEASURE_PROBE)' \
'WT_BOOTUPDATE_PROBE=$(WT_BOOTUPDATE_PROBE)' \
'WT_MAX_GUESTS=$(WT_MAX_GUESTS)' \
'WT_CO_STACK_SIZE=$(WT_CO_STACK_SIZE)' \
'WT_SPM_STACK_SIZE=$(WT_SPM_STACK_SIZE)' \
'WT_WOLFCRYPT_SP_ASM=$(WT_WOLFCRYPT_SP_ASM)' \
'WT_WOLFCRYPT_ARMASM=$(WT_WOLFCRYPT_ARMASM)' \
'WT_WOLFCRYPT_STM32_HASH=$(WT_WOLFCRYPT_STM32_HASH)' \
Expand Down Expand Up @@ -1535,6 +1550,7 @@ $(SECURE_ELF) $(SECURE_MAP) $(ARCH_LINK_OUTPUTS) &: $(ALL_SECURE_OBJS) $(SECURE_
$(CC) $(SECURE_CFLAGS) \
$(TARGET_LDFLAGS) \
-Wl,--defsym=WT_VNET_DATA_LENGTH=$(WT_VNET_DATA_LENGTH) \
-Wl,--defsym=WT_SPM_STACK_SIZE=$(WT_SPM_STACK_SIZE) \
-Wl,-T$(SECURE_LD) \
-Wl,--gc-sections -Wl,-Map=$(SECURE_MAP),--cref \
$(WT_LTO_LDFLAGS) $(WT_EXTRA_LDFLAGS) \
Expand Down
4 changes: 4 additions & 0 deletions port/mimxrt700/platform_mimxrt700.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,10 @@ void wt_platform_panic(void)

void wt_platform_system_reset(void)
{
/* Drop the Secure stack limits first: the next boot starts wolfBoot on
* its own stack below MSPLIM_S, and a core that carried the limit over
* the reset would fault its first push. */
__asm volatile("movs r0, #0\n msr msplim, r0\n msr psplim, r0" ::: "r0");
wt_dsb();
WT_SCB_AIRCR_S = WT_SCB_AIRCR_SYSRESETREQ;
wt_dsb();
Expand Down
4 changes: 4 additions & 0 deletions port/mimxrt700/secure.ld
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ MEMORY {
}

_estack = ORIGIN(RAM) + LENGTH(RAM);
/* Secure main stack: MSPLIM_S is set to _sstack at reset. */
_sstack = _estack - WT_SPM_STACK_SIZE;
_sidata = LOADADDR(.data);

SECTIONS {
Expand Down Expand Up @@ -321,4 +323,6 @@ SECTIONS {
"conformance data/bss overflows into the reserved MMIO holes")
ASSERT(ORIGIN(SPSTACKS) == (ORIGIN(CONFDATA) + LENGTH(CONFDATA)),
"SP secure stacks must sit directly above the conformance data window")
ASSERT(_ebss <= _sstack,
"secure .bss reaches the SPM main stack (WT_SPM_STACK_SIZE)")
}
15 changes: 15 additions & 0 deletions port/stm32h563/platform_stm32h563.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@ volatile void* wt_platform_boot_handoff_region(size_t* size)
return (volatile void*)WT_BOOT_HANDOFF_ADDRESS;
}

#if defined(WT_BUSFAULT_NEG_PROBE) && (WT_BUSFAULT_NEG_PROBE == 1)
/* The 32 KiB past the end of physical SRAM3 (0x300A0000) is unmapped on the
* H563, so an MPU-permitted read there is a precise BusFault on silicon. */
void wt_platform_busfault_probe_region(wt_memory_region_t* region)
{
region->base = 0x300A0000u;
region->size = 0x00008000u;
region->attributes = WT_MEM_ATTR_READ;
}
#endif

static void wt_clock_init(void)
{
uint32_t reg;
Expand Down Expand Up @@ -678,6 +689,10 @@ void wt_platform_system_reset(void)
spins < 0x00200000u) {
spins++;
}
/* Drop the Secure stack limits first: the next boot starts wolfBoot on
* its own stack below MSPLIM_S, and a core that carried the limit over
* the reset would fault its first push. */
__asm volatile("movs r0, #0\n msr msplim, r0\n msr psplim, r0" ::: "r0");
wt_dsb();
WT_SCB_AIRCR_S = WT_SCB_AIRCR_SYSRESETREQ;
wt_dsb();
Expand Down
5 changes: 3 additions & 2 deletions src/arch/armv8m/coroutine_armv8m.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,11 @@ void SVC_Handler(void)
"bne 2f \n"
/* Internal guest-return is privileged MSP-thread-only: a PSP-origin
* caller is a Secure Partition attempting the scheduler's own SVC,
* which fails the platform closed instead of restoring SPM state. */
* a PROGRAMMER ERROR that panics that partition alone. */
"tst lr, #4 \n"
"beq wt_armv8m_svc_guest_return \n"
"b wt_platform_panic \n"
"mov r0, r2 \n"
"b wt_spm_svc_reject \n"
"2: \n"
"cmp r3, #0x01 \n"
"bne 1f \n"
Expand Down
Loading
Loading