refactor(core/sec): make the boot header portable and lift it out of stm32
What changed, and why it matters
This commit is a code cleanup that moves a small piece of boot-header parsing code out of an STM32-specific folder so it can also be used in emulator and test builds. It changes function arguments from raw flash addresses (uint32_t numbers) to generic pointers, and fixes one accidental use of a security-boolean value where a null pointer was intended. The commit message and diff show no intended behavior change on real devices.
No security action required. Treat as ordinary refactoring review; verify the new size check and pointer API compile cleanly across supported targets and emulators.
Security signals we found
Refactor only: commit message explicitly states 'Pure refactor -- no functional change on any device'
Pointer API change removes uint32_t-to-pointer casts, which is a defensive portability improvement
Added code_size upper-bound validation against NONBOARDLOADER_MAXSIZE - hdr->header_size
Fixed semantically incorrect return secfalse in pointer-returning function to return NULL
No changelog entry, consistent with non-security cleanup
Evidence from the diff
The patch refactors boot_header.c to be MCU-agnostic by replacing uint32_t flash-address parameters with uintptr_t/const void* pointers (boot_header_auth_get, boot_header_calc_merkle_root, bootloader_area_needs_update). It adds an upper-bound size check on hdr->code_size and a static assertion, and corrects a return secfalse to return NULL in a pointer-returning function. The build script now compiles boot_header.c for the emulator and for any feature=boot_ucb build, enabling host-side bootloader emulation and tests. boot_ucb.c and boot_image.c remain under image/stm32/ because they still touch flash directly.
Changed components
core/embed/sec/image/boot_header.ccore/embed/sec/image/inc/sec/boot_header.hcore/embed/sec/image/build.rscore/embed/projects/boardloader/main.cInspect captured patch +27 / −19
### core/embed/projects/boardloader/main.c
@@ -181,7 +181,8 @@ static void try_bootloader_update(void) {
// Check if the new bootloader is the same as the old one
// (just prevents unnecessary flash erase/write)
- if (sectrue != bootloader_area_needs_update(hdr, code_address)) {
+ if (sectrue !=
+ bootloader_area_needs_update(hdr, code_address, BOOTLOADER_START)) {
return;
}
### core/embed/sec/image/boot_header.c
@@ -139,8 +139,8 @@ static const boot_header_merkle_proof_t* boot_header_get_merkle_proof(
return proof;
}
-const boot_header_auth_t* boot_header_auth_get(uint32_t address) {
- boot_header_auth_t* hdr = (boot_header_auth_t*)address;
+const boot_header_auth_t* boot_header_auth_get(uintptr_t header) {
+ const boot_header_auth_t* hdr = (const boot_header_auth_t*)header;
// Check if the header starts with the magic
if (hdr->magic != BOOT_HEADER_MAGIC_TRZQ) {
@@ -172,13 +172,16 @@ const boot_header_auth_t* boot_header_auth_get(uint32_t address) {
}
// Check if bootloader code size is within reasonable limits
- if (hdr->code_size < SIZE_8K) {
+ _Static_assert(NONBOARDLOADER_MAXSIZE >= SIZE_64K,
+ "non-boardloader area smaller than the maximum header size");
+ if (hdr->code_size < SIZE_8K ||
+ hdr->code_size > NONBOARDLOADER_MAXSIZE - hdr->header_size) {
return NULL;
}
// Check if the hardware model and revision match
if (hdr->hw_model != HW_MODEL || hdr->hw_revision != HW_REVISION) {
- return secfalse;
+ return NULL;
}
// Check if the header contains a valid Merkle proof
@@ -219,8 +222,7 @@ const boot_header_unauth_t* boot_header_unauth_get(
return unauth;
}
-void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
- uint32_t code_address,
+void boot_header_calc_merkle_root(const boot_header_auth_t* hdr, uintptr_t code,
merkle_proof_node_t* root) {
IMAGE_HASH_CTX ctx;
@@ -229,7 +231,7 @@ void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
// Hash the bootloader code
IMAGE_HASH_INIT(&ctx);
- IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)code_address, hdr->code_size);
+ IMAGE_HASH_UPDATE(&ctx, (const uint8_t*)code, hdr->code_size);
IMAGE_HASH_FINAL(&ctx, root->bytes);
// Hash the authenticated part of the header
@@ -258,12 +260,12 @@ void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
}
secbool bootloader_area_needs_update(const boot_header_auth_t* hdr,
- uint32_t code_address) {
- boot_header_auth_t* prev_hdr = (boot_header_auth_t*)BOOTLOADER_START;
+ uintptr_t code, uintptr_t prev_header) {
+ const boot_header_auth_t* prev_hdr = (const boot_header_auth_t*)prev_header;
if (hdr->header_size == prev_hdr->header_size &&
hdr->code_size == prev_hdr->code_size &&
(memcmp(hdr, prev_hdr, hdr->header_size) == 0) &&
- (memcmp((const uint8_t*)code_address,
+ (memcmp((const uint8_t*)code,
(const uint8_t*)prev_hdr + prev_hdr->header_size,
hdr->code_size) == 0)) {
return secfalse;
### core/embed/sec/image/build.rs
@@ -7,11 +7,15 @@ pub fn def_module(lib: &mut CLibrary) -> Result<()> {
lib.add_define("USE_SECMON_VERIFICATION", Some("1"));
}
+ if cfg!(feature = "boot_ucb") {
+ lib.add_source("image/boot_header.c");
+ }
+
if cfg!(feature = "emulator") {
lib.add_source("image/unix/boot_ucb.c")
} else if cfg!(feature = "mcu_stm32") {
if cfg!(feature = "boot_ucb") {
- lib.add_sources(["image/stm32/boot_header.c", "image/stm32/boot_ucb.c"]);
+ lib.add_source("image/stm32/boot_ucb.c");
// USE_BOOT_UCB symbol is already define in sys layer
}
lib.add_sources(["image/stm32/boot_image.c"]);
### core/embed/sec/image/inc/sec/boot_header.h
@@ -148,10 +148,10 @@ typedef struct __attribute__((packed)) {
*
* Checks the magic number, header size, code size, hardware model and revision
*
- * @param address Address of the boot header in flash memory
+ * @param header Address of the boot header
* @return Pointer to the boot header if valid, NULL otherwise.
*/
-const boot_header_auth_t* boot_header_auth_get(uint32_t address);
+const boot_header_auth_t* boot_header_auth_get(uintptr_t header);
/**
* Gets pointer to the unauthenticated part of the boot header.
@@ -170,11 +170,10 @@ const boot_header_unauth_t* boot_header_unauth_get(
* boot header and the Merkle tree path.
*
* @param hdr Pointer to the boot header
- * @param code_address Address of the bootloader code in flash memory
+ * @param code Address of the bootloader code
* @param root Pointer to the output Merkle root node
*/
-void boot_header_calc_merkle_root(const boot_header_auth_t* hdr,
- uint32_t code_address,
+void boot_header_calc_merkle_root(const boot_header_auth_t* hdr, uintptr_t code,
merkle_proof_node_t* root);
/**
@@ -197,8 +196,10 @@ secbool boot_header_check_signature(const boot_header_auth_t* hdr,
* it returns sectrue, otherwise secfalse.
*
* @param hdr Pointer to the new boot header
- * @param code_address Address of the new bootloader code in flash memory
+ * @param code Address of the new bootloader code
+ * @param prev_header Address of the installed boot header; the installed code
+ * is taken to follow it, at its own `header_size`
* @return secbool indicating whether the boot header and code need update
*/
secbool bootloader_area_needs_update(const boot_header_auth_t* hdr,
- uint32_t code_address);
+ uintptr_t code, uintptr_t prev_header);Why this scored 13/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.