feat(core): move secmon stack/data to ecc sram
What changed, and why it matters
This commit rearranges where the Trezor firmware's security monitor (a small privileged component) keeps its private stack and data. Previously, that protected memory sat in a region that was not covered by the hardware's error-correcting RAM. The change moves it into ECC-protected SRAM and splits the non-secure RAM description into two separate chunks so the memory protection tables can cover the gap correctly. It is a hardening change rather than a fix for an active, exploitable bug, but it removes a reliability/integrity risk for sensitive security-monitor data.
Treat as a hardening improvement. Verify that the new SECMON_RAM address actually falls within ECC-protected SRAM on the STM32U5, confirm SAU region boundaries align with physical memory map, and run regression tests for secure/non-secure context switching and boot.
Security signals we found
Relocation of privileged security-monitor RAM into ECC-protected SRAM
Splitting non-secure RAM into two discontiguous regions in TrustZone SAU configuration
Enabling previously disabled SAU region 7 for GFXMMU virtual buffers
Linker/runtime memory macros updated to match new layout
Evidence from the diff
The patch updates memory layout headers and linker scripts for D002 and T3W1 models, plus TrustZone configuration code on STM32U5. Key changes: (1) SECMON_RAM is relocated from 0x30260000 to 0x300C0000 and MAIN_RAM moves to 0x20260000; (2) NONSECURE_RAM is split into NONSECURE_RAM1 (0x20000200, 768K-512) and NONSECURE_RAM2 (0x200D0000, 768K+832K+64K) to reflect the physical SRAM layout; (3) the SAU (Security Attribution Unit) now uses two regions for non-secure RAM instead of one, and the previously disabled SAU region 7 is now used for GFXMMU virtual buffers; (4) startup/runtime RAM region macros are updated to enumerate the two non-secure blocks and place SECMON_RAM in the runtime list. The title says the intent is to move the security monitor stack/data into ECC SRAM.
Changed components
Trezor Core firmware memory layout (D002 and T3W1 models)Security monitor (secmon) stack/data regionSTM32U5 TrustZone SAU configurationStartup/runtime RAM region definitionsInspect captured patch +60 / −47
diff --git a/core/embed/models/D002/memory_secmon.h b/core/embed/models/D002/memory_secmon.h
index d8c57144a..455ddb53a 100644
--- a/core/embed/models/D002/memory_secmon.h
+++ b/core/embed/models/D002/memory_secmon.h
@@ -91,14 +91,17 @@
#define BOOTARGS_START (0x30000000)
#define BOOTARGS_SIZE 0x200
-#define NONSECURE_RAM_START (0x20000200)
-#define NONSECURE_RAM_SIZE ((768 + 64 + 768 + 832) * 1024 - 512)
+#define NONSECURE_RAM1_START (0x20000200)
+#define NONSECURE_RAM1_SIZE (768 * 1024 - 512)
+
+#define NONSECURE_RAM2_START (0x200D0000)
+#define NONSECURE_RAM2_SIZE ((768 + 832 + 64) * 1024)
#define FB1_RAM_START (0x20000200)
#define FB1_RAM_SIZE (768 * 1024 - 512)
-#define MAIN_RAM_START (0x200C0000)
-#define MAIN_RAM_SIZE (64 * 1024)
+#define SECMON_RAM_START (0x300C0000)
+#define SECMON_RAM_SIZE (64 * 1024)
#define FB2_RAM_START (0x200D0000)
#define FB2_RAM_SIZE (768 * 1024)
@@ -106,8 +109,8 @@
#define AUX1_RAM_START (0x20190000)
#define AUX1_RAM_SIZE (832 * 1024)
-#define SECMON_RAM_START (0x30260000)
-#define SECMON_RAM_SIZE (64 * 1024)
+#define MAIN_RAM_START (0x20260000)
+#define MAIN_RAM_SIZE (64 * 1024)
// misc
#define CODE_ALIGNMENT 0x400
diff --git a/core/embed/models/D002/memory_secmon.ld b/core/embed/models/D002/memory_secmon.ld
index 4f8c3eea3..37d043a7f 100644
--- a/core/embed/models/D002/memory_secmon.ld
+++ b/core/embed/models/D002/memory_secmon.ld
@@ -50,17 +50,19 @@ STORAGE_2_SECTOR_START = 0x1f0;
STORAGE_2_SECTOR_END = 0x1ff;
BOOTARGS_START = 0x30000000;
BOOTARGS_SIZE = 0x200;
-NONSECURE_RAM_START = 0x20000200;
-NONSECURE_RAM_SIZE = 0x25fe00;
+NONSECURE_RAM1_START = 0x20000200;
+NONSECURE_RAM1_SIZE = 0xbfe00;
+NONSECURE_RAM2_START = 0x200d0000;
+NONSECURE_RAM2_SIZE = 0x1a0000;
FB1_RAM_START = 0x20000200;
FB1_RAM_SIZE = 0xbfe00;
-MAIN_RAM_START = 0x200c0000;
-MAIN_RAM_SIZE = 0x10000;
+SECMON_RAM_START = 0x300c0000;
+SECMON_RAM_SIZE = 0x10000;
FB2_RAM_START = 0x200d0000;
FB2_RAM_SIZE = 0xc0000;
AUX1_RAM_START = 0x20190000;
AUX1_RAM_SIZE = 0xd0000;
-SECMON_RAM_START = 0x30260000;
-SECMON_RAM_SIZE = 0x10000;
+MAIN_RAM_START = 0x20260000;
+MAIN_RAM_SIZE = 0x10000;
CODE_ALIGNMENT = 0x400;
COREAPP_ALIGNMENT = 0x2000;
diff --git a/core/embed/models/T3W1/memory_secmon.h b/core/embed/models/T3W1/memory_secmon.h
index c6d2e921b..ce2c56de9 100644
--- a/core/embed/models/T3W1/memory_secmon.h
+++ b/core/embed/models/T3W1/memory_secmon.h
@@ -91,14 +91,17 @@
#define BOOTARGS_START (0x30000000)
#define BOOTARGS_SIZE 0x200
-#define NONSECURE_RAM_START (0x20000200)
-#define NONSECURE_RAM_SIZE ((768 + 64 + 768 + 832) * 1024 - 512)
+#define NONSECURE_RAM1_START (0x20000200)
+#define NONSECURE_RAM1_SIZE (768 * 1024 - 512)
+
+#define NONSECURE_RAM2_START (0x200D0000)
+#define NONSECURE_RAM2_SIZE ((768 + 832 + 64) * 1024)
#define FB1_RAM_START (0x20000200)
#define FB1_RAM_SIZE (768 * 1024 - 512)
-#define MAIN_RAM_START (0x200C0000)
-#define MAIN_RAM_SIZE (64 * 1024)
+#define SECMON_RAM_START (0x300C0000)
+#define SECMON_RAM_SIZE (64 * 1024)
#define FB2_RAM_START (0x200D0000)
#define FB2_RAM_SIZE (768 * 1024)
@@ -106,8 +109,8 @@
#define AUX1_RAM_START (0x20190000)
#define AUX1_RAM_SIZE (832 * 1024)
-#define SECMON_RAM_START (0x30260000)
-#define SECMON_RAM_SIZE (64 * 1024)
+#define MAIN_RAM_START (0x20260000)
+#define MAIN_RAM_SIZE (64 * 1024)
// misc
#define CODE_ALIGNMENT 0x400
diff --git a/core/embed/models/T3W1/memory_secmon.ld b/core/embed/models/T3W1/memory_secmon.ld
index b01af1321..b8da54395 100644
--- a/core/embed/models/T3W1/memory_secmon.ld
+++ b/core/embed/models/T3W1/memory_secmon.ld
@@ -50,17 +50,19 @@ STORAGE_2_SECTOR_START = 0x1f0;
STORAGE_2_SECTOR_END = 0x1ff;
BOOTARGS_START = 0x30000000;
BOOTARGS_SIZE = 0x200;
-NONSECURE_RAM_START = 0x20000200;
-NONSECURE_RAM_SIZE = 0x25fe00;
+NONSECURE_RAM1_START = 0x20000200;
+NONSECURE_RAM1_SIZE = 0xbfe00;
+NONSECURE_RAM2_START = 0x200d0000;
+NONSECURE_RAM2_SIZE = 0x1a0000;
FB1_RAM_START = 0x20000200;
FB1_RAM_SIZE = 0xbfe00;
-MAIN_RAM_START = 0x200c0000;
-MAIN_RAM_SIZE = 0x10000;
+SECMON_RAM_START = 0x300c0000;
+SECMON_RAM_SIZE = 0x10000;
FB2_RAM_START = 0x200d0000;
FB2_RAM_SIZE = 0xc0000;
AUX1_RAM_START = 0x20190000;
AUX1_RAM_SIZE = 0xd0000;
-SECMON_RAM_START = 0x30260000;
-SECMON_RAM_SIZE = 0x10000;
+MAIN_RAM_START = 0x20260000;
+MAIN_RAM_SIZE = 0x10000;
CODE_ALIGNMENT = 0x400;
COREAPP_ALIGNMENT = 0x2000;
diff --git a/core/embed/sys/linker/inc/sys/linker_utils.h b/core/embed/sys/linker/inc/sys/linker_utils.h
index c13e73db8..389c2ece8 100644
--- a/core/embed/sys/linker/inc/sys/linker_utils.h
+++ b/core/embed/sys/linker/inc/sys/linker_utils.h
@@ -108,16 +108,17 @@ typedef struct {
#ifdef SECMON
#undef MEMREGION_ALL_RUNTIME_RAM
-#define MEMREGION_ALL_RUNTIME_RAM \
- ({ \
- (memregion_t){ \
- .block = \
- { \
- MEMBLOCK(NONSECURE_RAM_START, NONSECURE_RAM_SIZE), \
- MEMBLOCK(BOOTARGS_START, BOOTARGS_SIZE), \
- MEMBLOCK(SECMON_RAM_START, SECMON_RAM_SIZE), \
- }, \
- }; \
+#define MEMREGION_ALL_RUNTIME_RAM \
+ ({ \
+ (memregion_t){ \
+ .block = \
+ { \
+ MEMBLOCK(BOOTARGS_START, BOOTARGS_SIZE), \
+ MEMBLOCK(NONSECURE_RAM1_START, NONSECURE_RAM1_SIZE), \
+ MEMBLOCK(SECMON_RAM_START, SECMON_RAM_SIZE), \
+ MEMBLOCK(NONSECURE_RAM2_START, NONSECURE_RAM2_SIZE), \
+ }, \
+ }; \
})
#endif // SECMON
@@ -131,14 +132,15 @@ typedef struct {
#undef MEMREGION_ALL_STARTUP_RAM
#undef MEMREGION_ALL_RUNTIME_RAM
-#define MEMREGION_ALL_STARTUP_RAM \
- ({ \
- (memregion_t){ \
- .block = \
- { \
- MEMBLOCK(NONSECURE_RAM_START, NONSECURE_RAM_SIZE), \
- }, \
- }; \
+#define MEMREGION_ALL_STARTUP_RAM \
+ ({ \
+ (memregion_t){ \
+ .block = \
+ { \
+ MEMBLOCK(NONSECURE_RAM1_START, NONSECURE_RAM1_SIZE), \
+ MEMBLOCK(NONSECURE_RAM2_START, NONSECURE_RAM2_SIZE), \
+ }, \
+ }; \
})
#define MEMREGION_ALL_RUNTIME_RAM MEMREGION_ALL_STARTUP_RAM
diff --git a/core/embed/sys/trustzone/stm32u5/trustzone.c b/core/embed/sys/trustzone/stm32u5/trustzone.c
index 1a6bff28f..d109472c4 100644
--- a/core/embed/sys/trustzone/stm32u5/trustzone.c
+++ b/core/embed/sys/trustzone/stm32u5/trustzone.c
@@ -81,10 +81,10 @@ static void tz_configure_sau(void) {
SET_REGION(1, NONSECURE_CODE_START, NONSECURE_CODE_SIZE, 0);
SET_REGION(2, ASSETS_START, ASSETS_MAXSIZE, 0);
SET_REGION(3, SGSTUBS_START, SGSTUBS_SIZE, 1);
- SET_REGION(4, NONSECURE_RAM_START, NONSECURE_RAM_SIZE, 0);
- SET_REGION(5, PERIPH_BASE_NS, SIZE_256M, 0);
- SET_REGION(6, GFXMMU_VIRTUAL_BUFFERS_BASE_NS, SIZE_16M, 0);
- DIS_REGION(7);
+ SET_REGION(4, NONSECURE_RAM1_START, NONSECURE_RAM1_SIZE, 0);
+ SET_REGION(5, NONSECURE_RAM2_START, NONSECURE_RAM2_SIZE, 0);
+ SET_REGION(6, PERIPH_BASE_NS, SIZE_256M, 0);
+ SET_REGION(7, GFXMMU_VIRTUAL_BUFFERS_BASE_NS, SIZE_16M, 0);
// clang-format on
SAU->CTRL = SAU_CTRL_ENABLE_Msk;
@@ -504,7 +504,8 @@ void tz_init(void) {
// Make part of the FLASH and SRAM regions non-secure
// so the kernel can access them
- tz_set_sram_unsecure(NONSECURE_RAM_START, NONSECURE_RAM_SIZE, true);
+ tz_set_sram_unsecure(NONSECURE_RAM1_START, NONSECURE_RAM1_SIZE, true);
+ tz_set_sram_unsecure(NONSECURE_RAM2_START, NONSECURE_RAM2_SIZE, true);
tz_set_flash_unsecure(NONSECURE_CODE_START, NONSECURE_CODE_SIZE, true);
tz_set_flash_unsecure(ASSETS_START, ASSETS_MAXSIZE, true);
Why this scored 31/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.