feat(core): allow running applets from SRAM
What changed, and why it matters
This commit changes the memory protection unit (MPU) configuration in Trezor's STM32U5 firmware so that small helper programs ('applets') can run from RAM (SRAM) in addition to running from flash memory. Previously, applets were assumed to live in flash. The change adds a new memory region type for executable SRAM and checks whether an applet's code starts in flash; if not, it marks the SRAM region as executable. This is a feature addition, not a bug fix, and there is no direct evidence in the commit that it fixes a security vulnerability. However, allowing code execution from SRAM can increase attack surface if an attacker can write to that RAM, because executable writable memory is generally less safe than execute-only flash.
Treat as a feature commit, not a confirmed security fix. Review the applet loading path to ensure SRAM applets are authenticated and integrity-checked before execution, and that SRAM regions are not writable by untrusted code during applet execution. If this change is part of a security response, request the vendor's advisory or incident reference.
Security signals we found
New executable SRAM memory region type added to MPU configuration
MPU region selection now depends on address range check for flash vs SRAM
Applets can now execute from SRAM, increasing potential writable-executable memory surface
No changelog entry and no security framing in commit message
Evidence from the diff
The patch modifies core/embed/sys/mpu/stm32u5/mpu.c. It introduces MPUX_TYPE_SRAM_CODE (4) with XN=LL_MPU_INSTRUCTION_ACCESS_ENABLE, attributes 1, and inner-shareable. It adds is_flash_address() to distinguish flash (secure and non-secure) from SRAM by comparing the top 8 bits of the address. In mpu_set_active_applet(), code regions code1 and code2 are now mapped as FLASH_CODE if their start address is in flash, otherwise as SRAM_CODE. This enables applets loaded into SRAM to execute. The commit message frames this as a feature (‘allow running applets from SRAM’) and includes ‘[no changelog]’. No CVE, advisory, or vendor security disclosure is present in the supplied materials.
Changed components
Trezor Core firmwareSTM32U5 MPU driver (core/embed/sys/mpu/stm32u5/mpu.c)Applet loader / execution subsystemInspect captured patch +26 / −2
diff --git a/core/embed/sys/mpu/stm32u5/mpu.c b/core/embed/sys/mpu/stm32u5/mpu.c
index 178c5c4a0..69f38a623 100644
--- a/core/embed/sys/mpu/stm32u5/mpu.c
+++ b/core/embed/sys/mpu/stm32u5/mpu.c
@@ -38,6 +38,7 @@
#define MPUX_TYPE_SRAM 1
#define MPUX_TYPE_PERIPHERAL 2
#define MPUX_TYPE_FLASH_DATA 3
+#define MPUX_TYPE_SRAM_CODE 4
const static struct {
uint32_t xn; // executable
@@ -69,6 +70,12 @@ const static struct {
.attr = LL_MPU_ATTRIBUTES_NUMBER3,
.sh = LL_MPU_ACCESS_NOT_SHAREABLE,
},
+ // 4 - SRAM CODE
+ {
+ .xn = LL_MPU_INSTRUCTION_ACCESS_ENABLE,
+ .attr = LL_MPU_ATTRIBUTES_NUMBER1,
+ .sh = LL_MPU_ACCESS_INNER_SHAREABLE,
+ },
};
static inline uint32_t mpu_permission_lookup(bool write, bool unpriv) {
@@ -133,6 +140,15 @@ _Static_assert(STORAGE_1_START + STORAGE_1_MAXSIZE == STORAGE_2_START,
_Static_assert(NORCOW_SECTOR_SIZE == STORAGE_1_MAXSIZE, "norcow misconfigured");
_Static_assert(NORCOW_SECTOR_SIZE == STORAGE_2_MAXSIZE, "norcow misconfigured");
+static inline bool is_flash_address(uint32_t addr) {
+ if ((addr >> 24) == (FLASH_BASE_NS >> 24)) {
+ return true;
+ } else if ((addr >> 24) == (FLASH_BASE_S >> 24)) {
+ return true;
+ }
+ return false;
+}
+
// PERIPH_SIZE covers both secure and non-secure peripherals
// 0x40000000 to 0x4FFFFFFF (256M) and
// 0x50000000 to 0x5FFFFFFF (256M).
@@ -332,7 +348,11 @@ void mpu_set_active_applet(const applet_layout_t* layout) {
if (layout != NULL) {
// clang-format off
if (layout->code1.start != 0 && layout->code1.size != 0) {
- SET_REGRUN( 2, layout->code1.start, layout->code1.size, FLASH_CODE, NO, YES );
+ if (is_flash_address(layout->code1.start)) {
+ SET_REGRUN( 2, layout->code1.start, layout->code1.size, FLASH_CODE, NO, YES );
+ } else {
+ SET_REGRUN( 2, layout->code1.start, layout->code1.size, SRAM_CODE, NO, YES );
+ }
} else {
DIS_REGION( 2 );
}
@@ -344,7 +364,11 @@ void mpu_set_active_applet(const applet_layout_t* layout) {
}
if (layout->code2.start != 0 && layout->code2.size != 0) {
- SET_REGRUN( 4, layout->code2.start, layout->code2.size, FLASH_CODE, NO, YES );
+ if (is_flash_address(layout->code2.start)) {
+ SET_REGRUN( 4, layout->code2.start, layout->code2.size, FLASH_CODE, NO, YES );
+ } else {
+ SET_REGRUN( 4, layout->code2.start, layout->code2.size, SRAM_CODE, NO, YES );
+ }
} else if (layout->data2.start != 0 && layout->data2.size != 0) {
SET_REGRUN( 4, layout->data2.start, layout->data2.size, SRAM, YES, YES );
} else {
Why this scored 37/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.