From eca9a20b3ba690a9627eded545eccfe7f5540289 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 2 Jul 2026 20:09:16 +0200 Subject: [PATCH] armored: harden fw_base against fault injection --- include/image.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++ src/image.c | 8 +++---- src/update_flash.c | 5 +++-- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/include/image.h b/include/image.h index 2d9aefa1..77eaee6a 100644 --- a/include/image.h +++ b/include/image.h @@ -173,6 +173,7 @@ struct wolfBoot_image { uint32_t sha_ok; uint32_t canary_FEEDCAFE; uint32_t not_sha_ok; + uintptr_t not_fw_base; /* complement of fw_base, for FI hardening */ uint32_t not_ext; /* image is no longer external */ }; @@ -229,6 +230,18 @@ static void NOINLINEFUNCTION wolfBoot_image_clear_sha_ok( img->not_sha_ok = 1UL; } +/** + * Records the image entry base together with its complement, so that a single + * fault on the pointer that do_boot() jumps through can be detected by + * FW_BASE_SANITY_CHECK() before the branch is taken. + */ +static void NOINLINEFUNCTION wolfBoot_image_set_fw_base( + struct wolfBoot_image *img, void *base) +{ + img->fw_base = (uint8_t *)base; + img->not_fw_base = ~(uintptr_t)base; +} + /** * Final sanity check, performed just before do_boot, or before starting an * update that has been verified. @@ -757,6 +770,40 @@ static void NOINLINEFUNCTION wolfBoot_image_clear_sha_ok( asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \ asm volatile("bne .-12") +/** + * Hardened assertion that the image entry base is consistent with its stored + * complement (fw_base == ~not_fw_base), performed immediately before do_boot() + * dereferences fw_base. A single fault on either the pointer or the loads + * fails the check safely (spins) instead of redirecting the boot jump. + */ +#define FW_BASE_SANITY_CHECK(p) \ + /* Redundant set of r2=0 */ \ + asm volatile("mov r2, #0":::"r2"); \ + asm volatile("mov r2, #0":::"r2"); \ + asm volatile("mov r2, #0":::"r2"); \ + asm volatile("mov r2, #0":::"r2"); \ + asm volatile("mov r2, #0":::"r2"); \ + /* r2 = fw_base, r0 = ~not_fw_base (== expected fw_base) */ \ + asm volatile("mov r2, %0" ::"r"((uintptr_t)(p)->fw_base):"r2"); \ + asm volatile("mov r0, %0" ::"r"((p)->not_fw_base):"r0"); \ + asm volatile("mvn r0, r0":::"r0"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("bne ."); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("bne .-4"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("bne .-8"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("cmp r2, r0":::"cc"); \ + asm volatile("bne .-12") + /** * ECC / Ed / PQ signature verification. * Those verify functions set an additional value 'p_res' @@ -1521,6 +1568,11 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok( { img->sha_ok = 0; } +static void UNUSEDFUNCTION wolfBoot_image_set_fw_base( + struct wolfBoot_image *img, void *base) +{ + img->fw_base = (uint8_t *)base; +} #define likely(x) (x) #define unlikely(x) (x) @@ -1560,6 +1612,8 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok( if ((p)->sha_ok != 1) \ wolfBoot_panic() +#define FW_BASE_SANITY_CHECK(p) do{} while(0) + #define CONFIRM_MASK_VALID(id, mask) \ if ((mask & (1UL << id)) != (1UL << id)) \ wolfBoot_panic() diff --git a/src/image.c b/src/image.c index 97841b66..3de4022c 100644 --- a/src/image.c +++ b/src/image.c @@ -1424,7 +1424,7 @@ int wolfBoot_open_image_address(struct wolfBoot_image *img, uint8_t *image) } #endif img->hdr_ok = 1; - img->fw_base = img->hdr + IMAGE_HEADER_SIZE; + wolfBoot_image_set_fw_base(img, img->hdr + IMAGE_HEADER_SIZE); #ifdef EXT_FLASH img->hdr_cache = image; #endif @@ -1488,7 +1488,7 @@ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part) if (part == PART_SWAP) { img->hdr = (void*)WOLFBOOT_PARTITION_SWAP_ADDRESS; img->hdr_ok = 1; - img->fw_base = img->hdr; + wolfBoot_image_set_fw_base(img, img->hdr); img->fw_size = WOLFBOOT_SECTOR_SIZE; return 0; } @@ -1507,7 +1507,7 @@ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part) if (ret < 0) return -1; img->hdr_ok = 1; - img->fw_base = img->hdr; + wolfBoot_image_set_fw_base(img, img->hdr); img->fw_size = (uint32_t)ret; return 0; } @@ -1620,7 +1620,7 @@ int wolfBoot_open_self_address(struct wolfBoot_image* img, uint8_t* hdr, return -1; } #endif - img->fw_base = image; + wolfBoot_image_set_fw_base(img, image); img->part = PART_SELF; img->hdr_ok = 1; diff --git a/src/update_flash.c b/src/update_flash.c index 9e433c26..b81d5251 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1270,7 +1270,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) wolfBoot_printf( "Scattered image correctly verified. Setting entry point to %lx\n", entry); - boot.fw_base = (void*)entry; + wolfBoot_image_set_fw_base(&boot, (void*)entry); #endif /* Direct Swap without power fail safety */ @@ -1618,7 +1618,7 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_printf( "Scattered image correctly verified. Setting entry point to %lx\n", entry); - boot.fw_base = (void*)entry; + wolfBoot_image_set_fw_base(&boot, (void*)entry); #endif @@ -1650,6 +1650,7 @@ void RAMFUNCTION wolfBoot_start(void) #endif #ifndef WOLFBOOT_SKIP_BOOT_VERIFY PART_SANITY_CHECK(&boot); + FW_BASE_SANITY_CHECK(&boot); #endif do_boot((void *)boot.fw_base); }