diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 7854aaa9166..d0ce66c0c5b 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -38,6 +38,7 @@ # CI specific options start from "--parallel-count xxx". could ignore when running locally - run_cmd idf-build-apps build -p tools/test_apps/system/clang_build_test + components/esp_security/test_apps/fault_assert_opt_check -t $IDF_TARGET --parallel-count ${CI_NODE_TOTAL:-1} --parallel-index ${CI_NODE_INDEX:-1} diff --git a/components/bootloader/subproject/main/ld/esp32p4/bootloader.memory.ld.in b/components/bootloader/subproject/main/ld/esp32p4/bootloader.memory.ld.in index e7b0611af49..e7870d8dcb1 100644 --- a/components/bootloader/subproject/main/ld/esp32p4/bootloader.memory.ld.in +++ b/components/bootloader/subproject/main/ld/esp32p4/bootloader.memory.ld.in @@ -31,7 +31,7 @@ #endif bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; -bootloader_iram_loader_seg_len = 0x7000; +bootloader_iram_loader_seg_len = 0x8000; bootloader_iram_seg_len = 0x2D00; /* Start of the lower region is determined by region size and the end of the higher region */ @@ -54,9 +54,9 @@ MEMORY * 3. Update SRAM_DRAM_END in components/esp_system/ld/esp32p4/memory.ld.in to the same value. */ #if !CONFIG_ESP32P4_SELECTS_REV_LESS_V3 -#define BOOTLOADER_IRAM_LOADER_SEG_START_EXPECTED 0x4FFAEFC0 +#define BOOTLOADER_IRAM_LOADER_SEG_START_EXPECTED 0x4FFADFC0 #else -#define BOOTLOADER_IRAM_LOADER_SEG_START_EXPECTED 0x4FF2CBD0 +#define BOOTLOADER_IRAM_LOADER_SEG_START_EXPECTED 0x4FF2BBD0 #endif ASSERT(bootloader_iram_loader_seg_start == BOOTLOADER_IRAM_LOADER_SEG_START_EXPECTED, "bootloader_iram_loader_seg_start inconsistent with SRAM_DRAM_END"); diff --git a/components/esp_common/include/esp_fault.h b/components/esp_common/include/esp_fault.h index 81c47741aa5..7ddd6ce43e9 100644 --- a/components/esp_common/include/esp_fault.h +++ b/components/esp_common/include/esp_fault.h @@ -1,13 +1,14 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#pragma once + +#include #include "sdkconfig.h" #include "esp_rom_sys.h" -#pragma once - #ifdef __cplusplus extern "C" { #endif @@ -19,6 +20,10 @@ extern "C" { * - Expands CONDITION multiple times (condition must have no side effects) * - Compiler is told all registers are invalid before evaluating CONDITION each time, to avoid a fault * causing a misread of a register used in all three evaluations of CONDITION. + * - The result of each evaluation is stored into a volatile variable and re-read before the branch. + * This prevents the compiler from constant-folding CONDITION and deleting the whole check when it + * has already proven the value - e.g. when this macro follows a normal "if (!cond) { ... }" check + * of the same value, which would otherwise silently remove the fault-injection protection. * - If CONDITION is ever false, a system reset is triggered. * * @note Place this macro after a "normal" check of CONDITION that will fail with a normal error @@ -40,13 +45,20 @@ extern "C" { * @param CONDITION A condition which will evaluate true unless an attacker used fault injection to skip or corrupt some other critical system calculation. * */ -#define ESP_FAULT_ASSERT(CONDITION) do { \ - asm volatile ("" ::: "memory"); \ - if(!(CONDITION)) _ESP_FAULT_RESET(); \ - asm volatile ("" ::: "memory"); \ - if(!(CONDITION)) _ESP_FAULT_RESET(); \ - asm volatile ("" ::: "memory"); \ - if(!(CONDITION)) _ESP_FAULT_RESET(); \ +#define ESP_FAULT_ASSERT(CONDITION) do { \ + bool esp_fault_assert_chk; \ + asm volatile ("" ::: "memory"); \ + esp_fault_assert_chk = (CONDITION); \ + asm volatile ("" : "+r"(esp_fault_assert_chk)); \ + if(!esp_fault_assert_chk) _ESP_FAULT_RESET(); \ + asm volatile ("" ::: "memory"); \ + esp_fault_assert_chk = (CONDITION); \ + asm volatile ("" : "+r"(esp_fault_assert_chk)); \ + if(!esp_fault_assert_chk) _ESP_FAULT_RESET(); \ + asm volatile ("" ::: "memory"); \ + esp_fault_assert_chk = (CONDITION); \ + asm volatile ("" : "+r"(esp_fault_assert_chk)); \ + if(!esp_fault_assert_chk) _ESP_FAULT_RESET(); \ } while(0) #if CONFIG_IDF_TARGET_ARCH_XTENSA diff --git a/components/esp_hal_security/esp32c5/include/hal/ecc_ll.h b/components/esp_hal_security/esp32c5/include/hal/ecc_ll.h index aa8d0738199..c72dfd4e875 100644 --- a/components/esp_hal_security/esp32c5/include/hal/ecc_ll.h +++ b/components/esp_hal_security/esp32c5/include/hal/ecc_ll.h @@ -61,6 +61,11 @@ static inline void ecc_ll_power_up(void) ESP_FAULT_ASSERT(REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0); } +static inline bool ecc_ll_mem_force_pd_is_clear(void) +{ + return REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0; +} + static inline void ecc_ll_power_down(void) { /* Power down the ECC peripheral */ diff --git a/components/esp_hal_security/esp32c61/include/hal/ecc_ll.h b/components/esp_hal_security/esp32c61/include/hal/ecc_ll.h index 717d81a7871..c9f6447dc9a 100644 --- a/components/esp_hal_security/esp32c61/include/hal/ecc_ll.h +++ b/components/esp_hal_security/esp32c61/include/hal/ecc_ll.h @@ -56,6 +56,11 @@ static inline void ecc_ll_power_up(void) ESP_FAULT_ASSERT(REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0); } +static inline bool ecc_ll_mem_force_pd_is_clear(void) +{ + return REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0; +} + static inline void ecc_ll_power_down(void) { /* Power down the ECC peripheral */ diff --git a/components/esp_hal_security/esp32h2/include/hal/ecc_ll.h b/components/esp_hal_security/esp32h2/include/hal/ecc_ll.h index c3904280287..00a897f4c93 100644 --- a/components/esp_hal_security/esp32h2/include/hal/ecc_ll.h +++ b/components/esp_hal_security/esp32h2/include/hal/ecc_ll.h @@ -58,6 +58,11 @@ static inline void ecc_ll_power_up(void) ESP_FAULT_ASSERT(REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0); } +static inline bool ecc_ll_mem_force_pd_is_clear(void) +{ + return REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0; +} + static inline void ecc_ll_power_down(void) { REG_CLR_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PU); diff --git a/components/esp_hal_security/esp32p4/include/hal/ecc_ll.h b/components/esp_hal_security/esp32p4/include/hal/ecc_ll.h index 34a9e98db7f..696c14b94a4 100644 --- a/components/esp_hal_security/esp32p4/include/hal/ecc_ll.h +++ b/components/esp_hal_security/esp32p4/include/hal/ecc_ll.h @@ -75,6 +75,11 @@ static inline void ecc_ll_power_up(void) ESP_FAULT_ASSERT(REG_GET_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PD) == 0); } +static inline bool ecc_ll_mem_force_pd_is_clear(void) +{ + return REG_GET_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PD) == 0; +} + static inline void ecc_ll_power_down(void) { /* Power down the ECC peripheral */ diff --git a/components/esp_rom/CMakeLists.txt b/components/esp_rom/CMakeLists.txt index 6deb2046dff..eed0702b9fb 100644 --- a/components/esp_rom/CMakeLists.txt +++ b/components/esp_rom/CMakeLists.txt @@ -24,7 +24,7 @@ else() "patches/esp_rom_efuse.c" "patches/esp_rom_gpio.c") - list(APPEND private_required_comp soc hal esp_hal_uart) + list(APPEND private_required_comp soc hal esp_hal_uart esp_hal_security) endif() if(CONFIG_IDF_TARGET_ARCH_XTENSA) diff --git a/components/esp_rom/esp32c5/ld/esp32c5.rom.ld b/components/esp_rom/esp32c5/ld/esp32c5.rom.ld index 08cb232ef8c..f7821cf87a3 100644 --- a/components/esp_rom/esp32c5/ld/esp32c5.rom.ld +++ b/components/esp_rom/esp32c5/ld/esp32c5.rom.ld @@ -413,8 +413,7 @@ esp_rom_km_huk_risk = 0x40000898; ets_emsa_pss_verify = 0x4000089c; ets_rsa_pss_verify = 0x400008a0; _rom_ets_ecdsa_verify = 0x400008a4; -ets_secure_boot_verify_bootloader_with_keys = 0x400008a8; -ets_secure_boot_verify_signature = 0x400008ac; +_rom_ets_secure_boot_verify_signature = 0x400008ac; ets_secure_boot_read_key_digests = 0x400008b0; ets_mgf1_sha256 = 0x400008b4; ets_secure_boot_revoke_public_key_digest = 0x400008b8; diff --git a/components/esp_rom/esp32c61/ld/esp32c61.rom.ld b/components/esp_rom/esp32c61/ld/esp32c61.rom.ld index 599fea694eb..ad5d692bb44 100644 --- a/components/esp_rom/esp32c61/ld/esp32c61.rom.ld +++ b/components/esp_rom/esp32c61/ld/esp32c61.rom.ld @@ -371,8 +371,7 @@ ets_efuse_usb_device_disabled = 0x40000808; /* Functions */ _rom_ets_ecdsa_verify = 0x40000810; -ets_secure_boot_verify_bootloader_with_keys = 0x40000814; -ets_secure_boot_verify_signature = 0x40000818; +_rom_ets_secure_boot_verify_signature = 0x40000818; ets_secure_boot_read_key_digests = 0x4000081c; ets_secure_boot_revoke_public_key_digest = 0x40000820; diff --git a/components/esp_rom/esp32h2/ld/esp32h2.rom.ld b/components/esp_rom/esp32h2/ld/esp32h2.rom.ld index 03b9c4a9c48..47690d2af98 100644 --- a/components/esp_rom/esp32h2/ld/esp32h2.rom.ld +++ b/components/esp_rom/esp32h2/ld/esp32h2.rom.ld @@ -362,8 +362,7 @@ ets_efuse_secure_boot_fast_wake_enabled = 0x40000830; ets_emsa_pss_verify = 0x40000834; ets_rsa_pss_verify = 0x40000838; _rom_ets_ecdsa_verify = 0x4000083c; -ets_secure_boot_verify_bootloader_with_keys = 0x40000840; -ets_secure_boot_verify_signature = 0x40000844; +_rom_ets_secure_boot_verify_signature = 0x40000844; ets_secure_boot_read_key_digests = 0x40000848; ets_secure_boot_revoke_public_key_digest = 0x4000084c; diff --git a/components/esp_rom/esp32h4/Kconfig.soc_caps.in b/components/esp_rom/esp32h4/Kconfig.soc_caps.in index 7b9f0c2fc46..9f22685b9a4 100644 --- a/components/esp_rom/esp32h4/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32h4/Kconfig.soc_caps.in @@ -71,10 +71,6 @@ config ESP_ROM_RAM_APP_NEEDS_MMU_INIT bool default y -config ESP_ROM_ECDSA_VERIFY_PATCH - bool - default y - config ESP_ROM_BOOTLOADER_OFFSET_FLASH hex default 0x2000 diff --git a/components/esp_rom/esp32h4/esp_rom_caps.h b/components/esp_rom/esp32h4/esp_rom_caps.h index 24f69478c7d..6b1f02bcde4 100644 --- a/components/esp_rom/esp32h4/esp_rom_caps.h +++ b/components/esp_rom/esp32h4/esp_rom_caps.h @@ -23,6 +23,5 @@ #define ESP_ROM_USB_OTG_NUM (-1) // No USB_OTG CDC in the ROM, set -1 for Kconfig usage. #define ESP_ROM_WDT_INIT_PATCH (1) // ROM version does not configure the clock #define ESP_ROM_RAM_APP_NEEDS_MMU_INIT (1) // ROM doesn't init cache MMU when it's a RAM APP, needs MMU hal to init -#define ESP_ROM_ECDSA_VERIFY_PATCH (1) // ROM ets_ecdsa_verify API requires a software patch #define ESP_ROM_BOOTLOADER_OFFSET_FLASH (0x2000) // Bootloader offset in flash determined by the ROM bootloader #define ESP_ROM_CACHE_WRITEBACK_NEEDS_SYNC_TWICE_NO_MAP (1) // ROM cache writeback related needs patch to avoid sync loss, no map parameter diff --git a/components/esp_rom/esp32h4/ld/esp32h4.rom.ld b/components/esp_rom/esp32h4/ld/esp32h4.rom.ld index 78a1b8252b5..44b02fa5e3d 100644 --- a/components/esp_rom/esp32h4/ld/esp32h4.rom.ld +++ b/components/esp_rom/esp32h4/ld/esp32h4.rom.ld @@ -385,7 +385,7 @@ esp_rom_recover_key = 0x400007cc; ***************************************/ /* Functions */ -_rom_ets_ecdsa_verify = 0x400007d0; +ets_ecdsa_verify = 0x400007d0; ets_secure_boot_verify_bootloader_with_keys = 0x400007d4; ets_secure_boot_verify_signature = 0x400007d8; ets_secure_boot_read_key_digests = 0x400007dc; diff --git a/components/esp_rom/esp32p4/ld/esp32p4.rom.eco0_4.ld b/components/esp_rom/esp32p4/ld/esp32p4.rom.eco0_4.ld index e6e7fa4a9fc..f0bfa948bad 100644 --- a/components/esp_rom/esp32p4/ld/esp32p4.rom.eco0_4.ld +++ b/components/esp_rom/esp32p4/ld/esp32p4.rom.eco0_4.ld @@ -451,8 +451,7 @@ esp_rom_km_huk_risk = 0x4fc0071c; ets_emsa_pss_verify = 0x4fc00720; ets_rsa_pss_verify = 0x4fc00724; _rom_ets_ecdsa_verify = 0x4fc00728; -ets_secure_boot_verify_bootloader_with_keys = 0x4fc0072c; -ets_secure_boot_verify_signature = 0x4fc00730; +_rom_ets_secure_boot_verify_signature = 0x4fc00730; ets_secure_boot_read_key_digests = 0x4fc00734; ets_secure_boot_revoke_public_key_digest = 0x4fc00738; diff --git a/components/esp_rom/esp32p4/ld/esp32p4.rom.ld b/components/esp_rom/esp32p4/ld/esp32p4.rom.ld index 720246d6cae..db0c692dc14 100644 --- a/components/esp_rom/esp32p4/ld/esp32p4.rom.ld +++ b/components/esp_rom/esp32p4/ld/esp32p4.rom.ld @@ -449,8 +449,7 @@ esp_rom_km_huk_risk = 0x4fc00710; ets_emsa_pss_verify = 0x4fc00714; ets_rsa_pss_verify = 0x4fc00718; _rom_ets_ecdsa_verify = 0x4fc0071c; -ets_secure_boot_verify_bootloader_with_keys = 0x4fc00720; -ets_secure_boot_verify_signature = 0x4fc00724; +_rom_ets_secure_boot_verify_signature = 0x4fc00724; ets_secure_boot_read_key_digests = 0x4fc00728; ets_secure_boot_revoke_public_key_digest = 0x4fc0072c; diff --git a/components/esp_rom/esp32s31/Kconfig.soc_caps.in b/components/esp_rom/esp32s31/Kconfig.soc_caps.in index f9ed1dab7d7..83db14746f3 100644 --- a/components/esp_rom/esp32s31/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32s31/Kconfig.soc_caps.in @@ -91,10 +91,6 @@ config ESP_ROM_PRINTS_LOCKUP_STATUS bool default y -config ESP_ROM_ECDSA_VERIFY_PATCH - bool - default y - config ESP_ROM_BOOTLOADER_OFFSET_FLASH hex default 0x2000 diff --git a/components/esp_rom/esp32s31/esp_rom_caps.h b/components/esp_rom/esp32s31/esp_rom_caps.h index a0606da0bdb..b4499994a0e 100644 --- a/components/esp_rom/esp32s31/esp_rom_caps.h +++ b/components/esp_rom/esp32s31/esp_rom_caps.h @@ -29,6 +29,5 @@ #define ESP_ROM_HAS_VERSION (1) // ROM has version/eco information #define ESP_ROM_HAS_OUTPUT_PUTC_FUNC (1) // ROM has esp_rom_output_putc (or ets_write_char_uart) #define ESP_ROM_PRINTS_LOCKUP_STATUS (1) // ROM bootloader already prints CPU lockup diagnostic status -#define ESP_ROM_ECDSA_VERIFY_PATCH (1) // ROM ets_ecdsa_verify API requires a software patch #define ESP_ROM_BOOTLOADER_OFFSET_FLASH (0x2000) // Bootloader offset in flash determined by the ROM bootloader #define ESP_ROM_CACHE_WRITEBACK_NEEDS_SYNC_TWICE_MAP (1) // ROM cache writeback related needs patch to avoid sync loss, need map parameter diff --git a/components/esp_rom/esp32s31/ld/esp32s31.rom.ld b/components/esp_rom/esp32s31/ld/esp32s31.rom.ld index 01d792adcff..bcb1c01162d 100644 --- a/components/esp_rom/esp32s31/ld/esp32s31.rom.ld +++ b/components/esp_rom/esp32s31/ld/esp32s31.rom.ld @@ -467,7 +467,7 @@ esp_rom_recover_key = 0x2f8008b8; /* Functions */ ets_emsa_pss_verify = 0x2f8008bc; ets_rsa_pss_verify = 0x2f8008c0; -_rom_ets_ecdsa_verify = 0x2f8008c4; +ets_ecdsa_verify = 0x2f8008c4; ets_secure_boot_verify_bootloader_with_keys = 0x2f8008c8; ets_secure_boot_verify_signature = 0x2f8008cc; ets_secure_boot_read_key_digests = 0x2f8008d0; diff --git a/components/esp_rom/patches/esp_rom_ecdsa.c b/components/esp_rom/patches/esp_rom_ecdsa.c index 0667fadca61..e967c994eeb 100644 --- a/components/esp_rom/patches/esp_rom_ecdsa.c +++ b/components/esp_rom/patches/esp_rom_ecdsa.c @@ -6,12 +6,15 @@ #include #include +#include #include +#include "sdkconfig.h" #include "esp_rom_caps.h" #if ESP_ROM_ECDSA_VERIFY_PATCH #include "soc/soc_caps.h" #include "esp_fault.h" +#include "hal/ecc_ll.h" #include "rom/ecdsa.h" #define VALID_MAGIC_OK 0x6A6A6A6AU @@ -69,6 +72,19 @@ static bool ecdsa_scalars_in_range(const uint32_t *r, const uint32_t *s, const u return true; } +// TODO: IDF-15721 +/* + * Runtime gate that decides whether the ROM ECDSA verification routines + * (ets_ecdsa_verify / ets_secure_boot_verify_signature) need the software patch + * in this file, or whether the ROM implementation is safe to call directly. + * + * When a future revision of one of these chips ships a ROM with these ECDSA + * verification issues fixed, add a ROM-version check here (e.g. compare the + * _rom_eco_version symbol against the first fixed ROM ECO version for that + * target) and return false for the fixed ROMs, so they skip the patch and jump + * straight to the _rom_ routine. + */ + extern int _rom_ets_ecdsa_verify(const uint8_t *key, const uint8_t *sig, ECDSA_CURVE curve_id, const uint8_t *image_digest, uint8_t *verified_digest); @@ -113,6 +129,9 @@ int ets_ecdsa_verify(const uint8_t *key, const uint8_t *sig, ESP_FAULT_ASSERT(ok && ret_status == VALID_MAGIC_OK); + ecc_ll_power_up(); + ESP_FAULT_ASSERT(ecc_ll_mem_force_pd_is_clear()); + int ret = _rom_ets_ecdsa_verify(key, sig, curve_id, image_digest, verified_digest); if (ret == 1) { @@ -125,4 +144,88 @@ int ets_ecdsa_verify(const uint8_t *key, const uint8_t *sig, return 0; } + +#if CONFIG_SECURE_BOOT_V2_ENABLED || CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT +#include "rom/secure_boot.h" + +#if CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME +static bool esp_rom_ecdsa_scalars_in_range(const uint8_t *r_le, const uint8_t *s_le, size_t component_len) +{ + const uint32_t *n; + int words; + switch (component_len) { + case 24: n = ecdsa_n_p192; words = 6; break; + case 32: n = ecdsa_n_p256; words = 8; break; +#if SOC_ECDSA_SUPPORT_CURVE_P384 + case 48: n = ecdsa_n_p384; words = 12; break; +#endif + default: return false; + } + + uint32_t r[12] = { 0 }; + uint32_t s[12] = { 0 }; + memcpy(r, r_le, component_len); + memcpy(s, s_le, component_len); + + uint32_t result = VALID_MAGIC_FAIL; + bool ok = ecdsa_scalars_in_range(r, s, n, words, &result); + if (!ok || result != VALID_MAGIC_OK) { + return false; + } + ESP_FAULT_ASSERT(ok && result == VALID_MAGIC_OK); + return true; +} + +static bool esp_rom_ecdsa_sig_block_in_range(const ets_secure_boot_sig_block_t *block) +{ + if (block->magic_byte != ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC) { + return true; + } + size_t component_len; + switch (block->ecdsa.key.curve_id) { + case ECDSA_CURVE_P256: component_len = 32; break; +#if SOC_ECDSA_SUPPORT_CURVE_P384 + case ECDSA_CURVE_P384: component_len = 48; break; +#endif + default: return false; + } + return esp_rom_ecdsa_scalars_in_range(&block->ecdsa.signature[0], + &block->ecdsa.signature[component_len], + component_len); +} +#endif /* CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME */ + +extern ets_secure_boot_status_t _rom_ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, + const uint8_t *image_digest, + const ets_secure_boot_key_digests_t *trusted_keys, + uint8_t *verified_digest); + +ets_secure_boot_status_t ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, + const uint8_t *image_digest, + const ets_secure_boot_key_digests_t *trusted_keys, + uint8_t *verified_digest) +{ +#if CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME + volatile ets_secure_boot_status_t range_status = SB_FAILED; + unsigned blocks_in_range = 0; + for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + if (esp_rom_ecdsa_sig_block_in_range(&sig->block[i])) { + blocks_in_range++; + } + } + if (blocks_in_range == SECURE_BOOT_NUM_BLOCKS) { + range_status = SB_SUCCESS; + } + if (range_status != SB_SUCCESS) { + return SB_FAILED; + } + ESP_FAULT_ASSERT(range_status == SB_SUCCESS); + ESP_FAULT_ASSERT(blocks_in_range == SECURE_BOOT_NUM_BLOCKS); + + ecc_ll_power_up(); + ESP_FAULT_ASSERT(ecc_ll_mem_force_pd_is_clear()); +#endif /* CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME */ + return _rom_ets_secure_boot_verify_signature(sig, image_digest, trusted_keys, verified_digest); +} +#endif /* CONFIG_SECURE_BOOT_V2_ENABLED || CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT */ #endif /* ESP_ROM_ECDSA_VERIFY_PATCH */ diff --git a/components/esp_security/test_apps/.build-test-rules.yml b/components/esp_security/test_apps/.build-test-rules.yml index 10b4c97116e..801962d89dc 100644 --- a/components/esp_security/test_apps/.build-test-rules.yml +++ b/components/esp_security/test_apps/.build-test-rules.yml @@ -6,3 +6,9 @@ components/esp_security/test_apps/crypto_drivers: depends_components: - esp_security - esp_hal_security + +components/esp_security/test_apps/fault_assert_opt_check: + enable: + - if: IDF_TARGET in ["esp32", "esp32c3"] # one Xtensa + one RISC-V + depends_components: + - esp_common diff --git a/components/esp_security/test_apps/fault_assert_opt_check/CMakeLists.txt b/components/esp_security/test_apps/fault_assert_opt_check/CMakeLists.txt new file mode 100644 index 00000000000..853b87d4eaf --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/CMakeLists.txt @@ -0,0 +1,19 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +set(COMPONENTS main) + +project(fault_assert_opt_check) + +# Regression guard: fail the build if ESP_FAULT_ASSERT() gets optimized away. +idf_build_get_property(python PYTHON) +add_custom_command( + TARGET ${CMAKE_PROJECT_NAME}.elf POST_BUILD + COMMAND ${python} "${CMAKE_CURRENT_SOURCE_DIR}/check_fault_asserts.py" + "$" "${CMAKE_OBJDUMP}" + COMMENT "Verifying ESP_FAULT_ASSERT() survived optimization" + VERBATIM) diff --git a/components/esp_security/test_apps/fault_assert_opt_check/README.md b/components/esp_security/test_apps/fault_assert_opt_check/README.md new file mode 100644 index 00000000000..1fb88efd154 --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C3 | +| ----------------- | ----- | -------- | diff --git a/components/esp_security/test_apps/fault_assert_opt_check/check_fault_asserts.py b/components/esp_security/test_apps/fault_assert_opt_check/check_fault_asserts.py new file mode 100644 index 00000000000..08dd6b5530e --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/check_fault_asserts.py @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +"""Post-build regression guard for ESP_FAULT_ASSERT(). + +Disassembles the test app and verifies that the ESP_FAULT_ASSERT() calls in the +known test functions still emit their reset blocks. One intact assert produces +three independent reset-on-failure paths, i.e. three references to +``esp_rom_software_reset_system`` in the function. If the macro ever regresses +and the optimizer folds the checks away, the count drops and this exits non-zero, +failing the build. + +Usage: check_fault_asserts.py +""" + +import re +import subprocess +import sys + +RESET_SYM = 'esp_rom_software_reset_system' + +# function name -> number of ESP_FAULT_ASSERT calls it contains (x3 reset blocks each) +EXPECTED = { + 'test_fa_guarded_flag': 1, + 'test_fa_guarded_status': 1, +} + +FUNC_RE = re.compile(r'^[0-9a-fA-F]+ <(.+)>:$') + + +def reset_counts(elf: str, objdump: str) -> dict: + dis = subprocess.run([objdump, '-d', elf], capture_output=True, text=True, check=True).stdout + counts: dict = {} + cur = None + for line in dis.splitlines(): + m = FUNC_RE.match(line) + if m: + cur = m.group(1) + counts.setdefault(cur, 0) + elif cur and RESET_SYM in line: + counts[cur] += 1 + return counts + + +def main() -> int: + if len(sys.argv) != 3: + print(__doc__) + return 2 + elf, objdump = sys.argv[1], sys.argv[2] + counts = reset_counts(elf, objdump) + + failed = False + for fn, n_asserts in EXPECTED.items(): + expected = 3 * n_asserts + found = counts.get(fn) + if found is None: + print(f'ERROR: {fn} not found in {elf} (renamed/removed?)') + failed = True + elif found < expected: + print( + f'ERROR: ESP_FAULT_ASSERT optimized away in {fn}: ' + f'{found} reset checks, expected {expected}. ' + f'See components/esp_common/include/esp_fault.h' + ) + failed = True + else: + print(f'OK: {fn} -> {found} reset checks ({found // 3} assert(s) x3)') + + if failed: + print('FAILED: ESP_FAULT_ASSERT regression check') + return 1 + print('PASSED: ESP_FAULT_ASSERT checks survived optimization') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/components/esp_security/test_apps/fault_assert_opt_check/main/CMakeLists.txt b/components/esp_security/test_apps/fault_assert_opt_check/main/CMakeLists.txt new file mode 100644 index 00000000000..d71200c0946 --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "test_fault_assert.c" + INCLUDE_DIRS ".") diff --git a/components/esp_security/test_apps/fault_assert_opt_check/main/test_fault_assert.c b/components/esp_security/test_apps/fault_assert_opt_check/main/test_fault_assert.c new file mode 100644 index 00000000000..920c09ecb01 --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/main/test_fault_assert.c @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Regression guard for ESP_FAULT_ASSERT() being silently optimised away. + * + * ESP_FAULT_ASSERT(C) must emit three independent "evaluate C -> reset if false" + * checks. When C is a value the optimiser can already prove (e.g. a flag pinned + * by a preceding "if (!C) return"), a naive implementation lets GCC constant-fold + * C and delete all three checks, removing the fault-injection protection with no + * warning. + * + * The functions below place ESP_FAULT_ASSERT in exactly that + * "proven-true cached value" shape. check_fault_asserts.py disassembles the built + * app and fails the build if any of them lost its reset blocks. Keep them + * noinline+used so each is an independent symbol the checker can find. + */ + +#include +#include "esp_fault.h" +#include "esp_attr.h" + +/* volatile so the initial value is opaque: only the early-return "proves" it, + * which is the precise condition that triggers the optimiser elimination. */ +volatile bool fa_test_flag = true; +volatile int fa_test_status = 0; +volatile int fa_test_sink; + +/* Cached bool guarded by an early return -> the original elimination case. */ +bool NOINLINE_ATTR test_fa_guarded_flag(void) +{ + bool valid = fa_test_flag; + if (!valid) { + return false; + } + ESP_FAULT_ASSERT(valid); + return true; +} + +/* Cached status compared to a constant, guarded by an early return. */ +int NOINLINE_ATTR test_fa_guarded_status(void) +{ + int status = fa_test_status; + if (status != 0) { + return status; + } + ESP_FAULT_ASSERT(status == 0); + return 0; +} + +void app_main(void) +{ + /* Reference the test functions so they are linked (not GC'd). */ + fa_test_sink = (int)test_fa_guarded_flag() + test_fa_guarded_status(); +} diff --git a/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.ci.opt_perf b/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.ci.opt_perf new file mode 100644 index 00000000000..4de2cc6f3ad --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.ci.opt_perf @@ -0,0 +1,2 @@ +# -O2 +CONFIG_COMPILER_OPTIMIZATION_PERF=y diff --git a/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.ci.opt_size b/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.ci.opt_size new file mode 100644 index 00000000000..f6da2fb1fbc --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.ci.opt_size @@ -0,0 +1,2 @@ +# -Os +CONFIG_COMPILER_OPTIMIZATION_SIZE=y diff --git a/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.defaults b/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.defaults new file mode 100644 index 00000000000..72548452f94 --- /dev/null +++ b/components/esp_security/test_apps/fault_assert_opt_check/sdkconfig.defaults @@ -0,0 +1,4 @@ +# The clang-built esp32 (Xtensa) bootloader is slightly larger than the GCC one and +# overflows the default 0x7000 limit; move the partition table offset to give it room. +# Harmless for GCC builds. +CONFIG_PARTITION_TABLE_OFFSET=0x9000 diff --git a/components/esp_system/ld/esp32p4/memory.ld.in b/components/esp_system/ld/esp32p4/memory.ld.in index 39eb420e31d..e9d1684c689 100644 --- a/components/esp_system/ld/esp32p4/memory.ld.in +++ b/components/esp_system/ld/esp32p4/memory.ld.in @@ -21,7 +21,7 @@ #if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM #define SRAM_END ULP_HP_MEM_START #else -#define SRAM_END 0x4FFAEFC0 /* 2nd stage bootloader iram_loader_seg start address */ +#define SRAM_END 0x4FFADFC0 /* 2nd stage bootloader iram_loader_seg start address */ #endif #define SRAM_SIZE SRAM_END - SRAM_START #else @@ -29,7 +29,7 @@ #if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM #define SRAM_LOW_END ULP_HP_MEM_START #else -#define SRAM_LOW_END 0x4FF2CBD0 /* 2nd stage bootloader iram_loader_seg start address */ +#define SRAM_LOW_END 0x4FF2BBD0 /* 2nd stage bootloader iram_loader_seg start address */ #endif #define SRAM_LOW_SIZE SRAM_LOW_END - SRAM_LOW_START diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default index e69de29bb2d..ccdf42de1af 100644 --- a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default @@ -0,0 +1,3 @@ +# Increasing TEE IRAM size +# 38KB +CONFIG_SECURE_TEE_IRAM_SIZE=0x9800 diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release index ebc7481de30..dacd12be7d7 100644 --- a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release @@ -2,8 +2,8 @@ # builds across various configurations - and is not intended for production use. # Reducing TEE IRAM size -# 29KB -CONFIG_SECURE_TEE_IRAM_SIZE=0x7400 +# 29.5KB +CONFIG_SECURE_TEE_IRAM_SIZE=0x7600 # TEE Secure Storage: Release mode CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe index 592e56ea491..d7965f52905 100644 --- a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe @@ -2,8 +2,8 @@ # builds across various configurations - and is not intended for production use. # Increasing TEE I/DRAM sizes -# 34KB -CONFIG_SECURE_TEE_IRAM_SIZE=0x8800 +# 38KB +CONFIG_SECURE_TEE_IRAM_SIZE=0x9800 # 22KB CONFIG_SECURE_TEE_DRAM_SIZE=0x5800 diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.tee_ota b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.tee_ota index 71e5dddc2d7..a9ce6c4279e 100644 --- a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.tee_ota +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.tee_ota @@ -16,3 +16,7 @@ CONFIG_SECURE_TEE_ATT_KEY_STR_ID="tee_att_keyN" # Enabling flash protection over SPI1 CONFIG_SECURE_TEE_EXT_FLASH_MEMPROT_SPI1=y + +# Increasing TEE IRAM size +# 38KB +CONFIG_SECURE_TEE_IRAM_SIZE=0x9800 diff --git a/components/spi_flash/test_apps/flash_encryption/partitions.csv b/components/spi_flash/test_apps/flash_encryption/partitions.csv index c941d8f4f1c..f5933e6f71f 100644 --- a/components/spi_flash/test_apps/flash_encryption/partitions.csv +++ b/components/spi_flash/test_apps/flash_encryption/partitions.csv @@ -1,5 +1,5 @@ # Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, 0x9000, 0x6000, -factory, 0, 0, 0x10000, 1M +nvs, data, nvs, , 0x6000, +factory, 0, 0, , 1M flash_test, data, fat, , 528K diff --git a/components/spi_flash/test_apps/flash_encryption/sdkconfig.defaults b/components/spi_flash/test_apps/flash_encryption/sdkconfig.defaults index d0ef86b66cf..c41595c93ec 100644 --- a/components/spi_flash/test_apps/flash_encryption/sdkconfig.defaults +++ b/components/spi_flash/test_apps/flash_encryption/sdkconfig.defaults @@ -1,4 +1,5 @@ CONFIG_ESP_TASK_WDT_EN=n +CONFIG_PARTITION_TABLE_OFFSET=0X9000 CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_SECURE_FLASH_ENC_ENABLED=y diff --git a/examples/system/efuse/sdkconfig.ci.virt_sb_v2_and_fe.esp32c5 b/examples/system/efuse/sdkconfig.ci.virt_sb_v2_and_fe.esp32c5 index 8622c2bd0ac..22226e8fdb6 100644 --- a/examples/system/efuse/sdkconfig.ci.virt_sb_v2_and_fe.esp32c5 +++ b/examples/system/efuse/sdkconfig.ci.virt_sb_v2_and_fe.esp32c5 @@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32c5" -CONFIG_PARTITION_TABLE_OFFSET=0xE000 +CONFIG_PARTITION_TABLE_OFFSET=0xF000 CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="test/partitions_efuse_emul.csv" diff --git a/examples/system/efuse/sdkconfig.ci.virt_sb_v2_ecdsa_p384_and_fe b/examples/system/efuse/sdkconfig.ci.virt_sb_v2_ecdsa_p384_and_fe index bf16d6cce60..71d9d0c0fc7 100644 --- a/examples/system/efuse/sdkconfig.ci.virt_sb_v2_ecdsa_p384_and_fe +++ b/examples/system/efuse/sdkconfig.ci.virt_sb_v2_ecdsa_p384_and_fe @@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32c5" -CONFIG_PARTITION_TABLE_OFFSET=0xE000 +CONFIG_PARTITION_TABLE_OFFSET=0xF000 CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="test/partitions_efuse_emul.csv" diff --git a/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32c5 b/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32c5 index 687b15f1e82..9a5317f9e20 100644 --- a/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32c5 +++ b/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32c5 @@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32c5" -CONFIG_PARTITION_TABLE_OFFSET=0xD000 +CONFIG_PARTITION_TABLE_OFFSET=0xE000 CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="test/partitions_efuse_emul.csv" diff --git a/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32p4 b/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32p4 index 5305d602c62..1b83c3d5946 100644 --- a/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32p4 +++ b/examples/system/efuse/sdkconfig.ci.virt_secure_boot_v2.esp32p4 @@ -2,7 +2,7 @@ CONFIG_IDF_TARGET="esp32p4" -CONFIG_PARTITION_TABLE_OFFSET=0xD000 +CONFIG_PARTITION_TABLE_OFFSET=0XE000 CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="test/partitions_efuse_emul.csv"