From 42c312e16e4a851d74eabbe1df316e4982277f50 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:35:11 +0200 Subject: [PATCH 01/41] Harden constant-time compare accumulators F/2565 --- src/image.c | 4 ++-- src/tpm.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/image.c b/src/image.c index cf434066f3..ae876877a7 100644 --- a/src/image.c +++ b/src/image.c @@ -61,7 +61,7 @@ static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4); int NOINLINEFUNCTION image_CT_compare( const uint8_t *expected, const uint8_t *actual, uint32_t len) { - uint8_t diff = 0; + volatile uint8_t diff = 0; uint32_t i; for (i = 0; i < len; i++) { @@ -2404,7 +2404,7 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset, static int keyslot_CT_hint_matches(const uint8_t *expected, const uint8_t *actual) { - uint8_t diff = 0; + volatile uint8_t diff = 0; uint32_t i; for (i = 0; i < WOLFBOOT_SHA_DIGEST_SIZE; i++) { diff --git a/src/tpm.c b/src/tpm.c index 23b3e05dd3..82ea6f6415 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -48,7 +48,7 @@ int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len) { uint32_t i; - uint8_t diff = 0; + volatile uint8_t diff = 0; for (i = 0; i < len; i++) { diff |= a[i] ^ b[i]; From 00892c264458c2aa7f01952f5dbccb9d21c56cb7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:38:41 +0200 Subject: [PATCH 02/41] Clamp unaligned SDHCI disk I/O spans F/2566 --- src/sdhci.c | 6 + tools/unit-tests/Makefile | 6 +- tools/unit-tests/unit-sdhci-disk-unaligned.c | 194 +++++++++++++++++++ 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 tools/unit-tests/unit-sdhci-disk-unaligned.c diff --git a/src/sdhci.c b/src/sdhci.c index 1073e3b9c9..8d7765349b 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -1550,6 +1550,9 @@ int disk_read(int drv, uint64_t start, uint32_t count, uint8_t *buf) start_offset != 0 || /* start not block aligned */ ((uintptr_t)buf % 4) != 0) /* buf not 4-byte aligned */ { + if (read_sz > (SDHCI_BLOCK_SIZE - start_offset)) { + read_sz = SDHCI_BLOCK_SIZE - start_offset; + } /* block read to temporary buffer */ status = sdhci_read(MMC_CMD17_READ_SINGLE, block_addr, tmp_block, SDHCI_BLOCK_SIZE); @@ -1602,6 +1605,9 @@ int disk_write(int drv, uint64_t start, uint32_t count, const uint8_t *buf) start_offset != 0 || /* start not block aligned */ ((uintptr_t)buf % 4) != 0) /* buf not 4-byte aligned */ { + if (write_sz > (SDHCI_BLOCK_SIZE - start_offset)) { + write_sz = SDHCI_BLOCK_SIZE - start_offset; + } /* read-modify-write for partial block */ status = sdhci_read(MMC_CMD17_READ_SINGLE, block_addr, tmp_block, SDHCI_BLOCK_SIZE); diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 47c6bb9d21..d891138126 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -52,7 +52,7 @@ TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128 unit-update-disk unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-store-sbrk \ unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \ - unit-sign-encrypted-output + unit-sdhci-disk-unaligned unit-sign-encrypted-output TESTS+=unit-tpm-check-rot-auth include unit-sign-encrypted-output.mkfrag @@ -207,6 +207,10 @@ unit-sdhci-response-bits: ../../include/target.h unit-sdhci-response-bits.c gcc -o $@ $^ $(CFLAGS) -ffunction-sections -fdata-sections $(LDFLAGS) \ -Wl,--gc-sections +unit-sdhci-disk-unaligned: ../../include/target.h unit-sdhci-disk-unaligned.c + gcc -o $@ $^ $(CFLAGS) -ffunction-sections -fdata-sections $(LDFLAGS) \ + -Wl,--gc-sections + unit-aes128: ../../include/target.h unit-extflash.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-sdhci-disk-unaligned.c b/tools/unit-tests/unit-sdhci-disk-unaligned.c new file mode 100644 index 0000000000..bd306c61b1 --- /dev/null +++ b/tools/unit-tests/unit-sdhci-disk-unaligned.c @@ -0,0 +1,194 @@ +/* unit-sdhci-disk-unaligned.c + * + * Unit tests for unaligned disk I/O via sdhci.c. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#define DISK_SDCARD 1 + +#include +#include +#include + +#include "sdhci.h" + +static uint32_t mock_regs[0x260 / sizeof(uint32_t)]; +static uint8_t mock_disk[SDHCI_BLOCK_SIZE * 4]; + +struct transfer_state { + int active; + int is_read; + uint32_t block_addr; + uint32_t word_index; + uint32_t word_count; +}; + +static struct transfer_state xfer; + +uint64_t hal_get_timer_us(void) +{ + static uint64_t now; + return ++now; +} + +uint32_t sdhci_reg_read(uint32_t offset) +{ + if (offset == SDHCI_SRS08 && xfer.active && xfer.is_read) { + uint32_t pos = xfer.block_addr * SDHCI_BLOCK_SIZE + (xfer.word_index * 4); + uint32_t val; + + memcpy(&val, &mock_disk[pos], sizeof(val)); + xfer.word_index++; + return val; + } + + return mock_regs[offset / sizeof(uint32_t)]; +} + +void sdhci_reg_write(uint32_t offset, uint32_t val) +{ + uint32_t *reg = &mock_regs[offset / sizeof(uint32_t)]; + + if (offset == SDHCI_SRS08 && xfer.active && !xfer.is_read) { + uint32_t pos = xfer.block_addr * SDHCI_BLOCK_SIZE + (xfer.word_index * 4); + + memcpy(&mock_disk[pos], &val, sizeof(val)); + xfer.word_index++; + return; + } + + if (offset == SDHCI_SRS11) { + *reg = val | (val & SDHCI_SRS11_ICE ? SDHCI_SRS11_ICS : 0); + *reg &= ~SDHCI_SRS11_RESET_DAT_CMD; + return; + } + + if (offset == SDHCI_SRS12) { + *reg &= ~val; + return; + } + + *reg = val; + + if (offset == SDHCI_SRS03) { + if (val & SDHCI_SRS03_DPS) { + xfer.active = 1; + xfer.is_read = (val & SDHCI_SRS03_DTDS) != 0; + xfer.block_addr = mock_regs[SDHCI_SRS02 / sizeof(uint32_t)]; + xfer.word_index = 0; + xfer.word_count = SDHCI_BLOCK_SIZE / sizeof(uint32_t); + mock_regs[SDHCI_SRS12 / sizeof(uint32_t)] |= xfer.is_read ? + (SDHCI_SRS12_BRR | SDHCI_SRS12_TC) : + (SDHCI_SRS12_BWR | SDHCI_SRS12_TC); + } + else { + mock_regs[SDHCI_SRS04 / sizeof(uint32_t)] = (1U << 8); + mock_regs[SDHCI_SRS12 / sizeof(uint32_t)] |= SDHCI_SRS12_CC; + } + } +} + +void sdhci_platform_init(void) +{ +} + +void sdhci_platform_irq_init(void) +{ +} + +void sdhci_platform_set_bus_mode(int is_emmc) +{ + (void)is_emmc; +} + +#include "../../src/sdhci.c" + +static void reset_mock_state(void) +{ + uint32_t i; + + memset(mock_regs, 0, sizeof(mock_regs)); + memset(&xfer, 0, sizeof(xfer)); + for (i = 0; i < sizeof(mock_disk); i++) { + mock_disk[i] = (uint8_t)i; + } +} + +START_TEST(test_disk_read_unaligned_spans_blocks) +{ + uint8_t out[SDHCI_BLOCK_SIZE]; + uint8_t expected[SDHCI_BLOCK_SIZE]; + + reset_mock_state(); + + memcpy(expected, &mock_disk[SDHCI_BLOCK_SIZE - 1], 1); + memcpy(expected + 1, &mock_disk[SDHCI_BLOCK_SIZE], SDHCI_BLOCK_SIZE - 1); + + ck_assert_int_eq(disk_read(0, SDHCI_BLOCK_SIZE - 1, sizeof(out), out), 0); + ck_assert_mem_eq(out, expected, sizeof(out)); +} +END_TEST + +START_TEST(test_disk_write_unaligned_spans_blocks) +{ + uint8_t in[SDHCI_BLOCK_SIZE]; + uint8_t before[sizeof(mock_disk)]; + uint32_t i; + + reset_mock_state(); + memcpy(before, mock_disk, sizeof(before)); + + for (i = 0; i < sizeof(in); i++) { + in[i] = (uint8_t)(0xA0U + (i & 0x1FU)); + } + + ck_assert_int_eq(disk_write(0, SDHCI_BLOCK_SIZE - 1, sizeof(in), in), 0); + ck_assert_mem_eq(mock_disk, before, SDHCI_BLOCK_SIZE - 1); + ck_assert_uint_eq(mock_disk[SDHCI_BLOCK_SIZE - 1], in[0]); + ck_assert_mem_eq(&mock_disk[SDHCI_BLOCK_SIZE], in + 1, SDHCI_BLOCK_SIZE - 1); + ck_assert_uint_eq(mock_disk[(2 * SDHCI_BLOCK_SIZE) - 1], + before[(2 * SDHCI_BLOCK_SIZE) - 1]); +} +END_TEST + +Suite *sdhci_disk_suite(void) +{ + Suite *s = suite_create("sdhci-disk"); + TCase *tc = tcase_create("unaligned"); + + tcase_add_test(tc, test_disk_read_unaligned_spans_blocks); + tcase_add_test(tc, test_disk_write_unaligned_spans_blocks); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int fails; + Suite *s = sdhci_disk_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + + return fails; +} From e10d9c1b1984f95e6b3c178ca147971087508337 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:44:29 +0200 Subject: [PATCH 03/41] Fix RAM fallback partition reselection F/2567 --- src/update_ram.c | 7 +- tools/unit-tests/Makefile | 12 +- tools/unit-tests/unit-update-ram-nofixed.c | 247 +++++++++++++++++++++ 3 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 tools/unit-tests/unit-update-ram-nofixed.c diff --git a/src/update_ram.c b/src/update_ram.c index b2f0d77212..f920685b3d 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -150,7 +150,12 @@ void RAMFUNCTION wolfBoot_start(void) else source_address = (uint32_t*)WOLFBOOT_PARTITION_UPDATE_ADDRESS; #else - active = wolfBoot_dualboot_candidate_addr((void**)&source_address); + if (active < 0) + active = wolfBoot_dualboot_candidate_addr((void**)&source_address); + else if (active == PART_BOOT) + source_address = (uint32_t*)hal_get_primary_address(); + else + source_address = (uint32_t*)hal_get_update_address(); #endif if (active < 0) { /* panic if no images available */ wolfBoot_printf("No valid image found!\n"); diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index d891138126..068e560bb5 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -48,7 +48,7 @@ TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128 unit-image unit-image-rsa unit-nvm unit-nvm-flagshome unit-enc-nvm \ unit-enc-nvm-flagshome unit-delta unit-update-flash unit-update-flash-delta \ unit-update-flash-self-update \ - unit-update-flash-enc unit-update-ram unit-pkcs11_store unit-psa_store unit-disk \ + unit-update-flash-enc unit-update-ram unit-update-ram-nofixed unit-pkcs11_store unit-psa_store unit-disk \ unit-update-disk unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-store-sbrk \ unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \ @@ -116,6 +116,13 @@ unit-update-ram:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \ -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT -DNO_XIP \ -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE +unit-update-ram-nofixed:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN \ + -DUNIT_TEST_AUTH -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH \ + -DPART_UPDATE_EXT -DPART_SWAP_EXT -DPART_BOOT_EXT -DWOLFBOOT_DUALBOOT \ + -DNO_XIP -DWOLFBOOT_NO_PARTITIONS -DUNIT_TEST_NO_FIXED_PARTITIONS \ + -DWOLFBOOT_RAMBOOT_MAX_SIZE=WOLFBOOT_PARTITION_SIZE \ + -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \ + -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-update-disk:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED \ -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-string:CFLAGS+=-fno-builtin @@ -295,6 +302,9 @@ unit-update-flash-enc: ../../include/target.h unit-update-flash.c unit-update-ram: ../../include/target.h unit-update-ram.c gcc -o $@ unit-update-ram.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) +unit-update-ram-nofixed: ../../include/target.h unit-update-ram-nofixed.c + gcc -o $@ unit-update-ram-nofixed.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) + unit-update-disk: ../../include/target.h unit-update-disk.c gcc -o $@ unit-update-disk.c $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-update-ram-nofixed.c b/tools/unit-tests/unit-update-ram-nofixed.c new file mode 100644 index 0000000000..39d047f9ac --- /dev/null +++ b/tools/unit-tests/unit-update-ram-nofixed.c @@ -0,0 +1,247 @@ +/* unit-update-ram-nofixed.c + * + * Reproducer for fallback selection in update_ram.c without fixed partitions. + */ +#ifndef WOLFBOOT_HASH_SHA256 + #define WOLFBOOT_HASH_SHA256 +#endif + +#define IMAGE_HEADER_SIZE 256 +#define MOCK_ADDRESS_UPDATE 0xCC000000 +#define MOCK_ADDRESS_BOOT 0xCD000000 +#define MOCK_ADDRESS_SWAP 0xCE000000 +#define NO_FORK 1 + +#include +#include +#include +#include +#include +#include + +#include "target.h" + +static __thread unsigned char + wolfboot_ram[WOLFBOOT_RAMBOOT_MAX_SIZE + IMAGE_HEADER_SIZE]; + +#define WOLFBOOT_LOAD_ADDRESS (((uintptr_t)wolfboot_ram) + IMAGE_HEADER_SIZE) +#define TEST_SIZE_SMALL 5300 +#define DIGEST_TLV_OFF_IN_HDR 28 + +#include "user_settings.h" +#include "wolfboot/wolfboot.h" + +#define wolfBoot_dualboot_candidate_addr wolfBoot_dualboot_candidate_addr_impl +#include "libwolfboot.c" +#undef wolfBoot_dualboot_candidate_addr + +static int dualboot_candidate_addr_calls; + +int wolfBoot_dualboot_candidate_addr(void** addr) +{ + dualboot_candidate_addr_calls++; + ck_assert_msg(dualboot_candidate_addr_calls == 1, + "wolfBoot_dualboot_candidate_addr() called %d times", + dualboot_candidate_addr_calls); + return wolfBoot_dualboot_candidate_addr_impl(addr); +} + +#include "update_ram.c" +#include "unit-mock-flash.c" +#include +#include + +int wolfBoot_staged_ok = 0; +const uint32_t *wolfBoot_stage_address = (uint32_t *)0xFFFFFFFF; + +void* hal_get_primary_address(void) +{ + return (void *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; +} + +void* hal_get_update_address(void) +{ + return (void *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS; +} + +void do_boot(const uint32_t *address) +{ + struct wolfBoot_image boot_image; + + if (wolfBoot_panicked) + return; + + wolfBoot_staged_ok++; + wolfBoot_stage_address = address; + ck_assert_uint_eq((uintptr_t)address, WOLFBOOT_LOAD_ADDRESS); + + memset(&boot_image, 0, sizeof(boot_image)); + ck_assert_int_eq(wolfBoot_open_image_address(&boot_image, wolfboot_ram), 0); + boot_image.hdr = wolfboot_ram; + boot_image.fw_base = (void *)(uintptr_t)WOLFBOOT_LOAD_ADDRESS; + boot_image.part = PART_BOOT; + boot_image.not_ext = 1; + ck_assert_int_eq(wolfBoot_verify_integrity(&boot_image), 0); +} + +int hal_flash_protect(haladdr_t address, int len) +{ + (void)address; + (void)len; + return 0; +} + +static void reset_mock_stats(void) +{ + wolfBoot_panicked = 0; + wolfBoot_staged_ok = 0; + dualboot_candidate_addr_calls = 0; +} + +static void prepare_flash(void) +{ + int ret; + + ret = mmap_file("/tmp/wolfboot-unit-ext-file-nofixed.bin", + (void *)(uintptr_t)MOCK_ADDRESS_UPDATE, + WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL); + ck_assert_int_ge(ret, 0); + ret = mmap_file("/tmp/wolfboot-unit-int-file-nofixed.bin", + (void *)(uintptr_t)MOCK_ADDRESS_BOOT, + WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE, NULL); + ck_assert_int_ge(ret, 0); + + ext_flash_unlock(); + ext_flash_erase(WOLFBOOT_PARTITION_BOOT_ADDRESS, + WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE); + ext_flash_erase(WOLFBOOT_PARTITION_UPDATE_ADDRESS, + WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE); + ext_flash_lock(); +} + +static void cleanup_flash(void) +{ + munmap((void *)WOLFBOOT_PARTITION_BOOT_ADDRESS, + WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE); + munmap((void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS, + WOLFBOOT_PARTITION_SIZE + IMAGE_HEADER_SIZE); +} + +static int add_payload(uint8_t part, uint32_t version, uint32_t size) +{ + uint32_t word; + uint16_t word16; + int i; + uint8_t *base = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS; + int ret; + wc_Sha256 sha; + uint8_t digest[SHA256_DIGEST_SIZE]; + + ret = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID); + if (ret != 0) + return ret; + + if (part == PART_UPDATE) + base = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS; + srandom(part); + + ext_flash_unlock(); + ext_flash_write((uintptr_t)base, "WOLF", 4); + ext_flash_write((uintptr_t)base + 4, (void *)&size, 4); + + word = 4 << 16 | HDR_VERSION; + ext_flash_write((uintptr_t)base + 8, (void *)&word, 4); + ext_flash_write((uintptr_t)base + 12, (void *)&version, 4); + + word = 2 << 16 | HDR_IMG_TYPE; + ext_flash_write((uintptr_t)base + 16, (void *)&word, 4); + word16 = HDR_IMG_TYPE_AUTH_NONE | HDR_IMG_TYPE_APP; + ext_flash_write((uintptr_t)base + 20, (void *)&word16, 2); + + ret = wc_Sha256Update(&sha, base, DIGEST_TLV_OFF_IN_HDR); + if (ret != 0) + return ret; + + size += IMAGE_HEADER_SIZE; + for (i = IMAGE_HEADER_SIZE; i < (int)size; i += 4) { + uint32_t rand_word = (random() << 16) | random(); + ext_flash_write((uintptr_t)base + i, (void *)&rand_word, 4); + } + for (i = IMAGE_HEADER_SIZE; i < (int)size; i += WOLFBOOT_SHA_BLOCK_SIZE) { + int len = WOLFBOOT_SHA_BLOCK_SIZE; + + if (((int)size - i) < len) + len = (int)size - i; + ret = wc_Sha256Update(&sha, base + i, len); + if (ret != 0) + return ret; + } + + ret = wc_Sha256Final(&sha, digest); + if (ret != 0) + return ret; + wc_Sha256Free(&sha); + + word = SHA256_DIGEST_SIZE << 16 | HDR_SHA256; + ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR, (void *)&word, 4); + ext_flash_write((uintptr_t)base + DIGEST_TLV_OFF_IN_HDR + 4, digest, + SHA256_DIGEST_SIZE); + ext_flash_lock(); + + return 0; +} + +START_TEST(test_invalid_update_falls_back_to_boot_without_reselect_loop) +{ + uint8_t bad_digest[SHA256_DIGEST_SIZE]; + + reset_mock_stats(); + prepare_flash(); + ck_assert_int_eq(add_payload(PART_BOOT, 1, TEST_SIZE_SMALL), 0); + ck_assert_int_eq(add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL), 0); + + memset(bad_digest, 0xBA, sizeof(bad_digest)); + ext_flash_unlock(); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + DIGEST_TLV_OFF_IN_HDR + 4, + bad_digest, sizeof(bad_digest)); + ext_flash_lock(); + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_staged_ok, 1); + ck_assert_ptr_eq(wolfBoot_stage_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS); + cleanup_flash(); +} +END_TEST + +static Suite *wolfboot_suite(void) +{ + Suite *s = suite_create("wolfboot-update-ram-nofixed"); + TCase *tc = tcase_create("fallback"); + + tcase_add_test(tc, test_invalid_update_falls_back_to_boot_without_reselect_loop); + tcase_set_timeout(tc, 5); + suite_add_tcase(s, tc); + + return s; +} + +int main(int argc, char *argv[]) +{ + int fails; + Suite *s; + SRunner *sr; + + argv0 = strdup(argv[0]); + (void)argc; + + s = wolfboot_suite(); + sr = srunner_create(s); +#if (NO_FORK == 1) + srunner_set_fork_status(sr, CK_NOFORK); +#endif + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} From dc7309c4fb03c6c76a1de982ebe4786d6cea8603 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:46:13 +0200 Subject: [PATCH 04/41] Fix TPM unseal handle cleanup F/2568 --- src/tpm.c | 13 +++++++++---- tools/unit-tests/unit-tpm-blob.c | 25 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/tpm.c b/src/tpm.c index 82ea6f6415..d5d6f8136e 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -1117,11 +1117,15 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, #endif /* if using password auth, set it otherwise use policy auth */ - if (authSz < 0) - return BAD_FUNC_ARG; + if (authSz < 0) { + rc = BAD_FUNC_ARG; + goto exit; + } if (auth != NULL && authSz > 0) { - if (authSz > (int)sizeof(seal_blob->handle.auth.buffer)) - return BAD_FUNC_ARG; + if (authSz > (int)sizeof(seal_blob->handle.auth.buffer)) { + rc = BAD_FUNC_ARG; + goto exit; + } seal_blob->handle.auth.size = authSz; memcpy(seal_blob->handle.auth.buffer, auth, authSz); wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &seal_blob->handle); @@ -1151,6 +1155,7 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, } TPM2_ForceZero(&unsealOut, sizeof(unsealOut)); +exit: wolfTPM2_UnloadHandle(&wolftpm_dev, &seal_blob->handle); wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle); wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session); diff --git a/tools/unit-tests/unit-tpm-blob.c b/tools/unit-tests/unit-tpm-blob.c index 0a543ff950..483c340844 100644 --- a/tools/unit-tests/unit-tpm-blob.c +++ b/tools/unit-tests/unit-tpm-blob.c @@ -40,6 +40,10 @@ static int oversized_priv_read_attempted; static int forcezero_calls; static word32 last_forcezero_len; static word32 last_pub_read_request_sz; +static int unload_handle_calls; +static int unload_seal_blob_calls; +static int unload_policy_session_calls; +static int unload_auth_key_calls; static uint8_t test_hdr[64]; static uint8_t test_modulus[256]; static uint8_t test_exponent_der[] = { 0xAA, 0x01, 0x00, 0x01, 0x7B }; @@ -119,7 +123,18 @@ int wolfTPM2_LoadKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEYBLOB* keyBlob, int wolfTPM2_UnloadHandle(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle) { (void)dev; - (void)handle; + unload_handle_calls++; + if (handle != NULL) { + if (handle->hndl == 1) { + unload_policy_session_calls++; + } + else if (handle->hndl == 2) { + unload_seal_blob_calls++; + } + else { + unload_auth_key_calls++; + } + } return 0; } @@ -488,6 +503,10 @@ static void setup(void) forcezero_calls = 0; last_forcezero_len = 0; last_pub_read_request_sz = 0; + unload_handle_calls = 0; + unload_seal_blob_calls = 0; + unload_policy_session_calls = 0; + unload_auth_key_calls = 0; memset(test_hdr, 0x22, sizeof(test_hdr)); memset(test_modulus, 0x33, sizeof(test_modulus)); } @@ -643,6 +662,8 @@ START_TEST(test_wolfBoot_unseal_blob_rejects_oversized_auth) secret, &secret_sz, auth, (int)sizeof(auth)); ck_assert_int_eq(rc, BAD_FUNC_ARG); + ck_assert_int_eq(unload_seal_blob_calls, 1); + ck_assert_int_eq(unload_policy_session_calls, 1); } END_TEST @@ -665,6 +686,8 @@ START_TEST(test_wolfBoot_unseal_blob_rejects_negative_auth_size) secret, &secret_sz, auth, -1); ck_assert_int_eq(rc, BAD_FUNC_ARG); + ck_assert_int_eq(unload_seal_blob_calls, 1); + ck_assert_int_eq(unload_policy_session_calls, 1); } END_TEST From 9265fe79843cabb52280cfcc9ac3a6ffea97ec42 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:48:59 +0200 Subject: [PATCH 05/41] loader: panic on TPM init failure F/2569 --- src/loader.c | 4 +- tools/unit-tests/Makefile | 8 +- tools/unit-tests/unit-loader-tpm-init.c | 98 +++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 tools/unit-tests/unit-loader-tpm-init.c diff --git a/src/loader.c b/src/loader.c index 4cdb8d6b34..76b0800bfe 100644 --- a/src/loader.c +++ b/src/loader.c @@ -124,7 +124,9 @@ int main(void) uart_send_current_version(); #endif #ifdef WOLFBOOT_TPM - wolfBoot_tpm2_init(); + if (wolfBoot_tpm2_init() != 0) { + wolfBoot_panic(); + } #endif #ifdef WOLFCRYPT_SECURE_MODE wcs_Init(); diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 068e560bb5..16cb6179f7 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -49,7 +49,7 @@ TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128 unit-enc-nvm-flagshome unit-delta unit-update-flash unit-update-flash-delta \ unit-update-flash-self-update \ unit-update-flash-enc unit-update-ram unit-update-ram-nofixed unit-pkcs11_store unit-psa_store unit-disk \ - unit-update-disk unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ + unit-update-disk unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-tpm-rsa-exp \ unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-store-sbrk \ unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \ unit-sdhci-disk-unaligned unit-sign-encrypted-output @@ -235,6 +235,12 @@ unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c -DUCODE0_ADDRESS=0 -ffunction-sections -fdata-sections $(LDFLAGS) \ -Wl,--gc-sections +unit-loader-tpm-init: ../../include/target.h unit-loader-tpm-init.c + gcc -o $@ $^ $(CFLAGS) -DWOLFBOOT_LOADER_MAIN -DWOLFBOOT_TPM \ + -DWOLFBOOT_HOOK_PANIC -DWOLFBOOT_SIGN_ECC256 \ + -DWOLFBOOT_HASH_SHA256 -ffunction-sections -fdata-sections \ + $(LDFLAGS) -Wl,--gc-sections + unit-mock-state: ../../include/target.h unit-mock-state.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-loader-tpm-init.c b/tools/unit-tests/unit-loader-tpm-init.c new file mode 100644 index 0000000000..50208ec828 --- /dev/null +++ b/tools/unit-tests/unit-loader-tpm-init.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include + +static int mock_tpm_init_rc; +static int start_calls; +static jmp_buf exit_env; + +void hal_init(void) +{ +} + +uint16_t spi_flash_probe(void) +{ + return 0; +} + +int wolfBoot_tpm2_init(void) +{ + return mock_tpm_init_rc; +} + +void wolfBoot_start(void) +{ + start_calls++; + longjmp(exit_env, 2); +} + +void wolfBoot_hook_panic(void) +{ + longjmp(exit_env, 1); +} + +#include "../../src/loader.c" + +static void setup(void) +{ + mock_tpm_init_rc = 0; + start_calls = 0; +} + +START_TEST(test_loader_panics_when_tpm_init_fails) +{ + int exit_reason; + + mock_tpm_init_rc = -1; + exit_reason = setjmp(exit_env); + if (exit_reason == 0) { + ck_assert_int_eq(loader_main(), 0); + } + + ck_assert_int_eq(exit_reason, 1); + ck_assert_int_eq(start_calls, 0); +} +END_TEST + +START_TEST(test_loader_starts_boot_when_tpm_init_succeeds) +{ + int exit_reason; + + exit_reason = setjmp(exit_env); + if (exit_reason == 0) { + ck_assert_int_eq(loader_main(), 0); + } + + ck_assert_int_eq(exit_reason, 2); + ck_assert_int_eq(start_calls, 1); +} +END_TEST + +static Suite *loader_suite(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("loader"); + tc = tcase_create("loader_main"); + tcase_add_checked_fixture(tc, setup, NULL); + tcase_add_test(tc, test_loader_panics_when_tpm_init_fails); + tcase_add_test(tc, test_loader_starts_boot_when_tpm_init_succeeds); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + Suite *s; + SRunner *sr; + int failed; + + s = loader_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + failed = srunner_ntests_failed(sr); + srunner_free(sr); + return failed == 0 ? 0 : 1; +} From f8cbad0bdfc49e0399de7fd58a7f1604c32fa8d8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:51:21 +0200 Subject: [PATCH 06/41] Add final sanity checks to boot paths F/2570 --- src/update_disk.c | 3 +++ src/update_flash_hwswap.c | 3 +++ src/update_ram.c | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/update_disk.c b/src/update_disk.c index 7e52feadd7..244d911582 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -559,6 +559,9 @@ void RAMFUNCTION wolfBoot_start(void) #ifdef WOLFBOOT_HOOK_BOOT wolfBoot_hook_boot(&os_image); #endif +#ifndef WOLFBOOT_SKIP_BOOT_VERIFY + PART_SANITY_CHECK(&os_image); +#endif #ifdef DISK_ENCRYPT disk_decrypted_header_clear(dec_hdr); disk_crypto_clear(); diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index 07c95862b7..65ce639184 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -113,6 +113,9 @@ void RAMFUNCTION wolfBoot_start(void) hal_prepare_boot(); #ifdef WOLFBOOT_HOOK_BOOT wolfBoot_hook_boot(&fw_image); +#endif +#ifndef WOLFBOOT_SKIP_BOOT_VERIFY + PART_SANITY_CHECK(&fw_image); #endif do_boot((void *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE)); } diff --git a/src/update_ram.c b/src/update_ram.c index f920685b3d..247299064c 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -406,6 +406,9 @@ void RAMFUNCTION wolfBoot_start(void) #ifdef WOLFBOOT_HOOK_BOOT wolfBoot_hook_boot(&os_image); #endif +#ifndef WOLFBOOT_SKIP_BOOT_VERIFY + PART_SANITY_CHECK(&os_image); +#endif #ifdef MMU do_boot((uint32_t*)load_address, (uint32_t*)dts_addr); From 41ffb2c6378b9b5737251ab9c36035ad3dcc2fce Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:55:26 +0200 Subject: [PATCH 07/41] Add partition overlap guards F/2571 --- include/MPLAB/target.h | 35 +++++++++++++++++++++++++++++++++++ include/target.h.in | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/include/MPLAB/target.h b/include/MPLAB/target.h index 721b210639..9f885efd18 100644 --- a/include/MPLAB/target.h +++ b/include/MPLAB/target.h @@ -90,6 +90,41 @@ #define WOLFBOOT_DTS_BOOT_ADDRESS #define WOLFBOOT_DTS_UPDATE_ADDRESS +#if !defined(WOLFBOOT_PART_USE_ARCH_OFFSET) && !defined(PULL_LINKER_DEFINES) + /* + * Only compare partitions that share the same internal flash address + * space. External partitions and runtime/linker-provided addresses are + * validated elsewhere. + */ + #if !defined(PART_BOOT_EXT) && !defined(PART_UPDATE_EXT) && \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ + WOLFBOOT_PARTITION_UPDATE_ADDRESS) && \ + (WOLFBOOT_PARTITION_BOOT_ADDRESS < \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) + #error "Boot and update partitions overlap" + #endif + + #if !defined(PART_BOOT_EXT) && !defined(PART_SWAP_EXT) && \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS != 0) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ + WOLFBOOT_PARTITION_SWAP_ADDRESS) && \ + (WOLFBOOT_PARTITION_BOOT_ADDRESS < \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + #error "Boot and swap partitions overlap" + #endif + + #if !defined(PART_UPDATE_EXT) && !defined(PART_SWAP_EXT) && \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0) && \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS != 0) && \ + ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ + WOLFBOOT_PARTITION_SWAP_ADDRESS) && \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS < \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + #error "Update and swap partitions overlap" + #endif +#endif + #endif /* WOLFBOOT_FIXED_PARTITIONS */ /* Load address in RAM for staged OS (update_ram only) */ diff --git a/include/target.h.in b/include/target.h.in index c4a113eb75..6d1d273dd3 100644 --- a/include/target.h.in +++ b/include/target.h.in @@ -110,6 +110,41 @@ #define WOLFBOOT_DTS_BOOT_ADDRESS @WOLFBOOT_DTS_BOOT_ADDRESS@ #define WOLFBOOT_DTS_UPDATE_ADDRESS @WOLFBOOT_DTS_UPDATE_ADDRESS@ +#if !defined(WOLFBOOT_PART_USE_ARCH_OFFSET) && !defined(PULL_LINKER_DEFINES) + /* + * Only compare partitions that share the same internal flash address + * space. External partitions and runtime/linker-provided addresses are + * validated elsewhere. + */ + #if !defined(PART_BOOT_EXT) && !defined(PART_UPDATE_EXT) && \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ + WOLFBOOT_PARTITION_UPDATE_ADDRESS) && \ + (WOLFBOOT_PARTITION_BOOT_ADDRESS < \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) + #error "Boot and update partitions overlap" + #endif + + #if !defined(PART_BOOT_EXT) && !defined(PART_SWAP_EXT) && \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS != 0) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ + WOLFBOOT_PARTITION_SWAP_ADDRESS) && \ + (WOLFBOOT_PARTITION_BOOT_ADDRESS < \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + #error "Boot and swap partitions overlap" + #endif + + #if !defined(PART_UPDATE_EXT) && !defined(PART_SWAP_EXT) && \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0) && \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS != 0) && \ + ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ + WOLFBOOT_PARTITION_SWAP_ADDRESS) && \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS < \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + #error "Update and swap partitions overlap" + #endif +#endif + #endif /* WOLFBOOT_FIXED_PARTITIONS */ #if !defined(WOLFBOOT_NO_LOAD_ADDRESS) From 6ce320ef1b25944a59c3483cf0723ccc6ba240a6 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:57:09 +0200 Subject: [PATCH 08/41] fix inverted emergency update branch hint F/2572 --- src/update_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/update_flash.c b/src/update_flash.c index 10b9196e1c..6eb00776ed 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1456,7 +1456,7 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_panic(); } else { /* Emergency update successful, try to re-open boot image */ - if (likely(((wolfBoot_open_image(&boot, PART_BOOT) < 0) || + if (unlikely(((wolfBoot_open_image(&boot, PART_BOOT) < 0) || (wolfBoot_verify_integrity(&boot) < 0) || (wolfBoot_verify_authenticity(&boot) < 0) ))) { From 6c9a0a0cf33b361de7088774c42ef92cd1ceedaf Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 07:59:19 +0200 Subject: [PATCH 09/41] Add final sanity check in library boot path F/2573 --- hal/library.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hal/library.c b/hal/library.c index 00e0b5230b..dddca8527c 100644 --- a/hal/library.c +++ b/hal/library.c @@ -83,6 +83,12 @@ void hal_prepare_boot(void) return; } +static void wolfBoot_panic(void) +{ + wolfBoot_printf("wolfBoot: PANIC!\n"); + exit('P'); +} + int do_boot(uint32_t* v) { wolfBoot_printf("booting %p" @@ -143,6 +149,9 @@ int wolfBoot_start(void) wolfBoot_printf("Firmware Valid\n"); +#ifndef WOLFBOOT_SKIP_BOOT_VERIFY + PART_SANITY_CHECK(&os_image); +#endif do_boot((uint32_t*)os_image.fw_base); exit: From b4b9ba3b98679359e7e12e6afad4f48601d280fc Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 08:01:16 +0200 Subject: [PATCH 10/41] Add rollback state assertion F/2574 --- tools/unit-tests/unit-update-flash.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 0d1de87c1b..49dd8f7ae3 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -774,6 +774,7 @@ START_TEST (test_invalid_sha) { START_TEST (test_emergency_rollback) { uint8_t testing_flags[5] = { IMG_STATE_TESTING, 'B', 'O', 'O', 'T' }; + uint8_t st = 0; reset_mock_stats(); prepare_flash(); add_payload(PART_BOOT, 2, TEST_SIZE_SMALL); @@ -788,6 +789,8 @@ START_TEST (test_emergency_rollback) { ck_assert(!wolfBoot_panicked); ck_assert(wolfBoot_staged_ok); ck_assert(wolfBoot_current_firmware_version() == 1); + ck_assert_int_eq(wolfBoot_get_partition_state(PART_BOOT, &st), 0); + ck_assert_uint_eq(st, IMG_STATE_SUCCESS); cleanup_flash(); } From 7048dd6169f1661cb5d0b8aa0cc8837100a7c607 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 12:21:14 +0200 Subject: [PATCH 11/41] Add update-size boundary coverage tests F/2575 --- tools/unit-tests/unit-update-flash.c | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 49dd8f7ae3..35ee64bedd 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -737,6 +737,40 @@ START_TEST (test_update_toolarge) { cleanup_flash(); } +START_TEST (test_update_max_size_minus_one_accepted) +{ + uint32_t boundary_ok = (uint32_t)(MAX_UPDATE_SIZE - 1U); + + reset_mock_stats(); + prepare_flash(); + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, boundary_ok); + wolfBoot_update_trigger(); + wolfBoot_start(); + ck_assert(!wolfBoot_panicked); + ck_assert(wolfBoot_staged_ok); + ck_assert(wolfBoot_current_firmware_version() == 2); + cleanup_flash(); +} +END_TEST + +START_TEST (test_update_max_size_rejected) +{ + uint32_t boundary_reject = (uint32_t)MAX_UPDATE_SIZE; + + reset_mock_stats(); + prepare_flash(); + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, boundary_reject); + wolfBoot_update_trigger(); + wolfBoot_start(); + ck_assert(!wolfBoot_panicked); + ck_assert(wolfBoot_staged_ok); + ck_assert(wolfBoot_current_firmware_version() == 1); + cleanup_flash(); +} +END_TEST + START_TEST (test_zero_size_update_rejected) { int ret; @@ -1056,6 +1090,8 @@ Suite *wolfboot_suite(void) tcase_add_test(invalid_update_type, test_invalid_update_type); tcase_add_test(invalid_update_auth_type, test_invalid_update_auth_type); tcase_add_test(update_toolarge, test_update_toolarge); + tcase_add_test(update_toolarge, test_update_max_size_minus_one_accepted); + tcase_add_test(update_toolarge, test_update_max_size_rejected); tcase_add_test(zero_size_update, test_zero_size_update_rejected); tcase_add_test(invalid_sha, test_invalid_sha); tcase_add_test(emergency_rollback, test_emergency_rollback); From 249ebaa66e5694a660641c2cd7a6ffdd830d8333 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 13:50:38 +0200 Subject: [PATCH 12/41] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6c7bad8e78..eccdcaee48 100644 --- a/.gitignore +++ b/.gitignore @@ -181,6 +181,9 @@ tools/unit-tests/unit-policy-create tools/unit-tests/unit-sign-encrypted-output tools/unit-tests/unit-update-flash-delta tools/unit-tests/unit-update-flash-self-update +tools/unit-tests/unit-loader-tpm-init +tools/unit-tests/unit-update-ram-nofixed + From c8a94d8ad52e2f36ba846056081dbc193239bf3c Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 13:52:15 +0200 Subject: [PATCH 13/41] Add erased-trailer partition state regression test F/2576 --- tools/unit-tests/unit-mock-state.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/unit-tests/unit-mock-state.c b/tools/unit-tests/unit-mock-state.c index 49ef47d5d6..6050ea13f0 100644 --- a/tools/unit-tests/unit-mock-state.c +++ b/tools/unit-tests/unit-mock-state.c @@ -342,6 +342,19 @@ START_TEST(test_wolfBoot_set_partition_state) END_TEST +START_TEST(test_wolfBoot_get_partition_state_rejects_erased_magic) +{ + uint8_t st = IMG_STATE_SUCCESS; + + mock_reset_partition_states(); + + /* With no magic trailer set, reads must fail. */ + ck_assert_int_eq(wolfBoot_get_partition_state(PART_UPDATE, &st), -1); + ck_assert_uint_eq(st, IMG_STATE_SUCCESS); + ck_assert_uint_eq(mock_state[PART_UPDATE].getstate_called, 0); +} +END_TEST + START_TEST(test_wolfBoot_misc_utils) { uint16_t word2 = 0xA0B1; @@ -373,6 +386,8 @@ Suite *wolfboot_suite(void) TCase* tcase_wolfBoot_set_partition_state = tcase_create("wolfBoot_set_partition_state"); tcase_set_timeout(tcase_wolfBoot_set_partition_state, 20); tcase_add_test(tcase_wolfBoot_set_partition_state, test_wolfBoot_set_partition_state); + tcase_add_test(tcase_wolfBoot_set_partition_state, + test_wolfBoot_get_partition_state_rejects_erased_magic); suite_add_tcase(s, tcase_wolfBoot_set_partition_state); TCase* tcase_wolfBoot_misc_utils = tcase_create("wolfBoot_misc_utils"); From 9421355aea3be917d712d55bd6ed7edf0200166f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 13:55:43 +0200 Subject: [PATCH 14/41] Add delta base-version unit coverage F/2577 --- tools/unit-tests/unit-update-flash.c | 121 +++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 35ee64bedd..379fe30d4c 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -38,7 +38,15 @@ #include "user_settings.h" #include "wolfboot/wolfboot.h" #include "libwolfboot.c" +#ifdef DELTA_UPDATES +#define wb_patch_init unit_test_wb_patch_init +#define wb_patch unit_test_wb_patch +#endif #include "update_flash.c" +#ifdef DELTA_UPDATES +#undef wb_patch_init +#undef wb_patch +#endif #include #include #include @@ -55,6 +63,30 @@ static void cleanup_flash(void); static int add_payload_type(uint8_t part, uint32_t version, uint32_t size, uint16_t img_type); +#ifdef DELTA_UPDATES +static int mock_wb_patch_init_calls = 0; + +int unit_test_wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, + uint8_t *patch, uint32_t psz) +{ + (void)bm; + (void)src; + (void)ssz; + (void)patch; + (void)psz; + mock_wb_patch_init_calls++; + return 0; +} + +int unit_test_wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len) +{ + (void)ctx; + (void)dst; + (void)len; + return 0; +} +#endif + #ifdef CUSTOM_ENCRYPT_KEY static int mock_get_encrypt_key_ret = 0; static int mock_set_encrypt_key_ret = 0; @@ -158,6 +190,9 @@ static void reset_mock_stats(void) #ifdef RAM_CODE arch_reboot_called = 0; #endif +#ifdef DELTA_UPDATES + mock_wb_patch_init_calls = 0; +#endif } static void clear_erase_stats(void) @@ -997,6 +1032,88 @@ START_TEST (test_delta_zero_size_erased_header_uses_recovery_heuristic) cleanup_flash(); } END_TEST + +START_TEST (test_delta_base_version_mismatch_rejected) +{ + struct wolfBoot_image boot, update, swap; + uint32_t word; + uint32_t delta_sz = 0; + uint32_t delta_base = 3; + int ret; + + reset_mock_stats(); + prepare_flash(); + + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + + ext_flash_unlock(); + word = (4u << 16) | HDR_IMG_DELTA_SIZE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, + (const uint8_t *)&delta_sz, sizeof(delta_sz)); + word = (4u << 16) | HDR_IMG_DELTA_BASE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, + (const uint8_t *)&delta_base, sizeof(delta_base)); + ext_flash_lock(); + + ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); + ck_assert_int_eq(wolfBoot_open_image(&update, PART_UPDATE), 0); + memset(&swap, 0, sizeof(swap)); + swap.part = PART_SWAP; + swap.hdr = (void *)(uintptr_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; + + ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0); + ck_assert_int_eq(ret, -1); + ck_assert_int_eq(mock_wb_patch_init_calls, 0); + + cleanup_flash(); +} +END_TEST + +START_TEST (test_delta_base_version_match_accepts) +{ + struct wolfBoot_image boot, update, swap; + uint32_t word; + uint32_t delta_sz = 0; + uint32_t delta_base = 1; + int ret; + + reset_mock_stats(); + prepare_flash(); + + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + + ext_flash_unlock(); + word = (4u << 16) | HDR_IMG_DELTA_SIZE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, + (const uint8_t *)&delta_sz, sizeof(delta_sz)); + word = (4u << 16) | HDR_IMG_DELTA_BASE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, + (const uint8_t *)&delta_base, sizeof(delta_base)); + ext_flash_lock(); + + ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); + ck_assert_int_eq(wolfBoot_open_image(&update, PART_UPDATE), 0); + memset(&swap, 0, sizeof(swap)); + swap.part = PART_SWAP; + swap.hdr = (void *)(uintptr_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; + + ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(mock_wb_patch_init_calls, 1); + + cleanup_flash(); +} +END_TEST #endif #endif @@ -1058,6 +1175,7 @@ Suite *wolfboot_suite(void) TCase *boot_success = tcase_create("Boot success state"); #ifdef DELTA_UPDATES TCase *delta_zero_size = tcase_create("Delta zero size"); + TCase *delta_base_version = tcase_create("Delta base version check"); #endif #ifdef RAM_CODE TCase *self_update_sameversion = tcase_create("Self update same version erased"); @@ -1105,6 +1223,8 @@ Suite *wolfboot_suite(void) #ifdef DELTA_UPDATES tcase_add_test(delta_zero_size, test_delta_zero_size_valid_header_rejected_without_recovery_heuristic); tcase_add_test(delta_zero_size, test_delta_zero_size_erased_header_uses_recovery_heuristic); + tcase_add_test(delta_base_version, test_delta_base_version_mismatch_rejected); + tcase_add_test(delta_base_version, test_delta_base_version_match_accepts); #endif #ifdef RAM_CODE tcase_add_test(self_update_sameversion, test_self_update_sameversion_erased); @@ -1140,6 +1260,7 @@ Suite *wolfboot_suite(void) suite_add_tcase(s, boot_success); #ifdef DELTA_UPDATES suite_add_tcase(s, delta_zero_size); + suite_add_tcase(s, delta_base_version); #endif #ifdef RAM_CODE suite_add_tcase(s, self_update_sameversion); From 194b36359bebe75a34481846e9e0c101b4c4f0c5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 13:58:03 +0200 Subject: [PATCH 15/41] Reset hdr_ok when image open fails F/2578 --- src/image.c | 8 ++++---- tools/unit-tests/unit-image.c | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/image.c b/src/image.c index ae876877a7..7b50db13c1 100644 --- a/src/image.c +++ b/src/image.c @@ -1383,9 +1383,7 @@ int wolfBoot_get_dts_size(void *dts_addr) */ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part) { -#ifdef MMU int ret; -#endif uint8_t *image; if (!img) return -1; @@ -1441,8 +1439,10 @@ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part) else image = (uint8_t *)img->hdr; img->hdr_ok = 1; - - return wolfBoot_open_image_address(img, image); + ret = wolfBoot_open_image_address(img, image); + if (ret != 0) + img->hdr_ok = 0; + return ret; } diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index fef59d2c95..10cd4ac975 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -835,6 +835,7 @@ START_TEST(test_open_image) ext_flash_erase(0, WOLFBOOT_SECTOR_SIZE); ret = wolfBoot_open_image(&img, PART_UPDATE); ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(img.hdr_ok, 0); /* Swap partition */ ret = wolfBoot_open_image(&img, PART_SWAP); From 6ef9e026d645a5ba28af09b2df2618d4a25c5495 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:00:08 +0200 Subject: [PATCH 16/41] test nvm sector flag invalid magic guard F/2579 --- tools/unit-tests/unit-nvm.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tools/unit-tests/unit-nvm.c b/tools/unit-tests/unit-nvm.c index 12d69a3c60..aa021ba9b0 100644 --- a/tools/unit-tests/unit-nvm.c +++ b/tools/unit-tests/unit-nvm.c @@ -330,6 +330,38 @@ START_TEST(test_partition_magic_write_stops_on_flash_write_error) } END_TEST +START_TEST(test_get_update_sector_flag_rejects_invalid_magic) +{ + int ret; + uint8_t st = 0xA5; + uint32_t *magic; + + ret = mmap_file("/tmp/wolfboot-unit-invalid-magic.bin", (void *)MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); +#ifdef FLAGS_HOME + ret = mmap_file("/tmp/wolfboot-unit-invalid-magic-int.bin", + (void *)MOCK_ADDRESS_BOOT, WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); +#endif + ret = mmap_file("/tmp/wolfboot-unit-invalid-magic-swap.bin", + (void *)MOCK_ADDRESS_SWAP, WOLFBOOT_SECTOR_SIZE, NULL); + ck_assert(ret >= 0); + + hal_flash_unlock(); + wolfBoot_erase_partition(PART_UPDATE); + + magic = get_partition_magic(PART_UPDATE); + *magic = 0xFFFFFFFF; + + ret = wolfBoot_get_update_sector_flag(0, &st); + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(st, 0xA5); + + hal_flash_lock(); +} +END_TEST + Suite *wolfboot_suite(void) { @@ -341,6 +373,8 @@ Suite *wolfboot_suite(void) tcase_add_test(nvm_select_fresh_sector, test_nvm_select_fresh_sector); tcase_add_test(nvm_select_fresh_sector, test_partition_magic_write_stops_on_flash_write_error); + tcase_add_test(nvm_select_fresh_sector, + test_get_update_sector_flag_rejects_invalid_magic); suite_add_tcase(s, nvm_select_fresh_sector); return s; From 871f03cab3ad244fa28e7ab3e11861a0398c3fd2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:01:34 +0200 Subject: [PATCH 17/41] Fix external image hdr_ok state on open failure F/2580 --- src/image.c | 6 +++++- tools/unit-tests/unit-image.c | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/image.c b/src/image.c index 7b50db13c1..991601aaf1 100644 --- a/src/image.c +++ b/src/image.c @@ -1451,6 +1451,7 @@ int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8_t* addr) { uint8_t* image; + int ret; if (img == NULL) return -1; @@ -1460,7 +1461,10 @@ int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, img->hdr_ok = 1; hdr_cpy_done = 0; /* reset hdr "open" flag */ image = fetch_hdr_cpy(img); - return wolfBoot_open_image_address(img, image); + ret = wolfBoot_open_image_address(img, image); + if (ret != 0) + img->hdr_ok = 0; + return ret; } #endif /* EXT_FLASH */ diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index 10cd4ac975..1334897de2 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -870,6 +870,15 @@ START_TEST(test_open_image) ck_assert_ptr_eq(img.fw_base, (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS + 256); + /* Invalid external header must keep hdr_ok cleared on failure */ + ext_flash_erase(0, WOLFBOOT_SECTOR_SIZE); + memset(&img, 0, sizeof(img)); + hdr_cpy_done = 0; + ret = wolfBoot_open_image_external(&img, PART_UPDATE, + (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(img.hdr_ok, 0); + /* Self header must reject sizes beyond the partition payload budget */ memset(self_hdr, 0xFF, sizeof(self_hdr)); ((uint32_t *)self_hdr)[0] = WOLFBOOT_MAGIC; From 2bd6274022bdec3b4b64ed506e9a877e8c0c0a84 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:09:59 +0200 Subject: [PATCH 18/41] Fix endian handling for delta TLV consumers F/2581 --- src/libwolfboot.c | 2 +- src/update_flash.c | 10 +- tools/unit-tests/unit-update-flash.c | 139 +++++++++++++++++++++++---- 3 files changed, 128 insertions(+), 23 deletions(-) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 8d52712ae9..e5a138c6e7 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1205,7 +1205,7 @@ uint32_t wolfBoot_get_blob_diffbase_version(uint8_t *blob) (void *)&delta_base) == 0) return 0; if (delta_base) - return *delta_base; + return im2n(*delta_base); return 0; } diff --git a/src/update_flash.c b/src/update_flash.c index 6eb00776ed..fd60efae82 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -577,6 +577,8 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, uint8_t delta_blk[DELTA_BLOCK_SIZE]; uint32_t *img_offset; uint32_t *img_size; + uint32_t delta_img_offset = 0; + uint32_t delta_img_size = 0; uint32_t total_size; WB_PATCH_CTX ctx; uint32_t cur_v, upd_v, delta_base_v; @@ -617,6 +619,9 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, &delta_base_hash, &delta_base_hash_sz) < 0) { return -1; } + delta_img_size = im2n(*img_size); + if (inverse) + delta_img_offset = im2n(*img_offset); cur_v = wolfBoot_current_firmware_version(); upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); @@ -653,7 +658,8 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, ((cur_v == upd_v) && (delta_base_v <= cur_v)) || ((cur_v == delta_base_v) && (upd_v >= cur_v))) { ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + - IMAGE_HEADER_SIZE, update->hdr + *img_offset, *img_size); + IMAGE_HEADER_SIZE, update->hdr + delta_img_offset, + delta_img_size); } else { wolfBoot_printf("Delta version check failed! " "Cur 0x%x, Upd 0x%x, Delta 0x%x\n", @@ -673,7 +679,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, ret = -1; } else { ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + IMAGE_HEADER_SIZE, - update->hdr + IMAGE_HEADER_SIZE, *img_size); + update->hdr + IMAGE_HEADER_SIZE, delta_img_size); } } if (ret < 0) diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 379fe30d4c..ed69720f74 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -63,8 +63,32 @@ static void cleanup_flash(void); static int add_payload_type(uint8_t part, uint32_t version, uint32_t size, uint16_t img_type); +static uint32_t host_to_img_u32(uint32_t val) +{ +#ifdef BIG_ENDIAN_ORDER + return (((val & 0x000000FFu) << 24) | + ((val & 0x0000FF00u) << 8) | + ((val & 0x00FF0000u) >> 8) | + ((val & 0xFF000000u) >> 24)); +#else + return val; +#endif +} + +static uint16_t host_to_img_u16(uint16_t val) +{ +#ifdef BIG_ENDIAN_ORDER + return (uint16_t)(((val & 0x00FFu) << 8) | + ((val & 0xFF00u) >> 8)); +#else + return val; +#endif +} + #ifdef DELTA_UPDATES static int mock_wb_patch_init_calls = 0; +static uint8_t *mock_wb_patch_init_patch = NULL; +static uint32_t mock_wb_patch_init_psz = 0; int unit_test_wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, uint8_t *patch, uint32_t psz) @@ -72,9 +96,9 @@ int unit_test_wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, (void)bm; (void)src; (void)ssz; - (void)patch; - (void)psz; mock_wb_patch_init_calls++; + mock_wb_patch_init_patch = patch; + mock_wb_patch_init_psz = psz; return 0; } @@ -192,6 +216,8 @@ static void reset_mock_stats(void) #endif #ifdef DELTA_UPDATES mock_wb_patch_init_calls = 0; + mock_wb_patch_init_patch = NULL; + mock_wb_patch_init_psz = 0; #endif } @@ -243,6 +269,10 @@ static int add_payload_type(uint8_t part, uint32_t version, uint32_t size, uint16_t img_type) { uint32_t word; + uint32_t magic = WOLFBOOT_MAGIC; + uint32_t size_img = host_to_img_u32(size); + uint32_t version_img = host_to_img_u32(version); + uint16_t img_type_img = host_to_img_u16(img_type); int i; uint8_t *base = (uint8_t *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; int ret; @@ -260,21 +290,21 @@ static int add_payload_type(uint8_t part, uint32_t version, uint32_t size, hal_flash_unlock(); - hal_flash_write((uintptr_t)base, "WOLF", 4); - printf("Written magic: \"WOLF\"\n"); + hal_flash_write((uintptr_t)base, (void *)&magic, 4); + printf("Written magic: 0x%08X\n", magic); - hal_flash_write((uintptr_t)base + 4, (void *)&size, 4); + hal_flash_write((uintptr_t)base + 4, (void *)&size_img, 4); printf("Written size: %u\n", size); /* Headers */ word = 4 << 16 | HDR_VERSION; hal_flash_write((uintptr_t)base + 8, (void *)&word, 4); - hal_flash_write((uintptr_t)base + 12, (void *)&version, 4); + hal_flash_write((uintptr_t)base + 12, (void *)&version_img, 4); printf("Written version: %u\n", version); word = 2 << 16 | HDR_IMG_TYPE; hal_flash_write((uintptr_t)base + 16, (void *)&word, 4); - hal_flash_write((uintptr_t)base + 20, (void *)&img_type, 2); + hal_flash_write((uintptr_t)base + 20, (void *)&img_type_img, 2); printf("Written img_type: %04X\n", img_type); /* Add 28B header to sha calculation */ @@ -929,7 +959,12 @@ END_TEST START_TEST (test_diffbase_version_reads) { + uint32_t magic = WOLFBOOT_MAGIC; uint32_t word; + uint32_t word_le; + uint32_t version_le; + uint32_t delta_base_le; + uint16_t img_type_le; uint32_t version = 0x01020304; uint32_t delta_base = 0x33445566; uint16_t img_type = HDR_IMG_TYPE_AUTH | HDR_IMG_TYPE_APP; @@ -939,27 +974,33 @@ START_TEST (test_diffbase_version_reads) ext_flash_unlock(); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS, - (const uint8_t *)"WOLF", 4); + (const uint8_t *)&magic, sizeof(magic)); + version_le = host_to_img_u32(version); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 4, - (const uint8_t *)&version, sizeof(version)); + (const uint8_t *)&version_le, sizeof(version_le)); word = (4u << 16) | HDR_VERSION; + word_le = word; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 8, - (const uint8_t *)&word, sizeof(word)); + (const uint8_t *)&word_le, sizeof(word_le)); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 12, - (const uint8_t *)&version, sizeof(version)); + (const uint8_t *)&version_le, sizeof(version_le)); word = (2u << 16) | HDR_IMG_TYPE; + word_le = word; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 16, - (const uint8_t *)&word, sizeof(word)); + (const uint8_t *)&word_le, sizeof(word_le)); + img_type_le = host_to_img_u16(img_type); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 20, - (const uint8_t *)&img_type, sizeof(img_type)); + (const uint8_t *)&img_type_le, sizeof(img_type_le)); word = (4u << 16) | HDR_IMG_DELTA_BASE; + word_le = word; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 24, - (const uint8_t *)&word, sizeof(word)); + (const uint8_t *)&word_le, sizeof(word_le)); + delta_base_le = host_to_img_u32(delta_base); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 28, - (const uint8_t *)&delta_base, sizeof(delta_base)); + (const uint8_t *)&delta_base_le, sizeof(delta_base_le)); ext_flash_lock(); ck_assert_uint_eq(wolfBoot_get_diffbase_version(PART_UPDATE), delta_base); @@ -1051,13 +1092,15 @@ START_TEST (test_delta_base_version_mismatch_rejected) word = (4u << 16) | HDR_IMG_DELTA_SIZE; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_sz); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, - (const uint8_t *)&delta_sz, sizeof(delta_sz)); + (const uint8_t *)&word, sizeof(word)); word = (4u << 16) | HDR_IMG_DELTA_BASE; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_base); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, - (const uint8_t *)&delta_base, sizeof(delta_base)); + (const uint8_t *)&word, sizeof(word)); ext_flash_lock(); ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); @@ -1078,7 +1121,7 @@ START_TEST (test_delta_base_version_match_accepts) { struct wolfBoot_image boot, update, swap; uint32_t word; - uint32_t delta_sz = 0; + uint32_t delta_sz = 0x00001020; uint32_t delta_base = 1; int ret; @@ -1092,13 +1135,15 @@ START_TEST (test_delta_base_version_match_accepts) word = (4u << 16) | HDR_IMG_DELTA_SIZE; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_sz); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, - (const uint8_t *)&delta_sz, sizeof(delta_sz)); + (const uint8_t *)&word, sizeof(word)); word = (4u << 16) | HDR_IMG_DELTA_BASE; ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_base); ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, - (const uint8_t *)&delta_base, sizeof(delta_base)); + (const uint8_t *)&word, sizeof(word)); ext_flash_lock(); ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); @@ -1110,6 +1155,59 @@ START_TEST (test_delta_base_version_match_accepts) ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0); ck_assert_int_eq(ret, 0); ck_assert_int_eq(mock_wb_patch_init_calls, 1); + ck_assert_uint_eq(mock_wb_patch_init_psz, delta_sz); + + cleanup_flash(); +} +END_TEST + +START_TEST (test_delta_inverse_values_passed_with_native_endian) +{ + struct wolfBoot_image boot, update, swap; + uint32_t word; + uint32_t delta_inverse_offset = 0x00001020; + uint32_t delta_inverse_size = 0x00002040; + uint32_t delta_base = 1; + int ret; + + reset_mock_stats(); + prepare_flash(); + + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + + ext_flash_unlock(); + word = (4u << 16) | HDR_IMG_DELTA_INVERSE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, + (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_inverse_offset); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, + (const uint8_t *)&word, sizeof(word)); + word = (4u << 16) | HDR_IMG_DELTA_INVERSE_SIZE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, + (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_inverse_size); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, + (const uint8_t *)&word, sizeof(word)); + word = (4u << 16) | HDR_IMG_DELTA_BASE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 80, + (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_base); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 84, + (const uint8_t *)&word, sizeof(word)); + ext_flash_lock(); + + ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); + ck_assert_int_eq(wolfBoot_open_image(&update, PART_UPDATE), 0); + memset(&swap, 0, sizeof(swap)); + swap.part = PART_SWAP; + swap.hdr = (void *)(uintptr_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; + + ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 1); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(mock_wb_patch_init_calls, 1); + ck_assert_ptr_eq(mock_wb_patch_init_patch, update.hdr + delta_inverse_offset); + ck_assert_uint_eq(mock_wb_patch_init_psz, delta_inverse_size); cleanup_flash(); } @@ -1225,6 +1323,7 @@ Suite *wolfboot_suite(void) tcase_add_test(delta_zero_size, test_delta_zero_size_erased_header_uses_recovery_heuristic); tcase_add_test(delta_base_version, test_delta_base_version_mismatch_rejected); tcase_add_test(delta_base_version, test_delta_base_version_match_accepts); + tcase_add_test(delta_base_version, test_delta_inverse_values_passed_with_native_endian); #endif #ifdef RAM_CODE tcase_add_test(self_update_sameversion, test_self_update_sameversion_erased); From bc4ec501d0df9845aa5a6a7b53b75d06f433fc7b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:12:55 +0200 Subject: [PATCH 19/41] delta: reject sector sizes that overflow 16-bit match length F/2582 --- src/delta.c | 6 ++++++ tools/unit-tests/unit-delta.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/delta.c b/src/delta.c index fd7674ee3e..145299fd5f 100644 --- a/src/delta.c +++ b/src/delta.c @@ -233,6 +233,12 @@ int wb_diff_get_sector_size(void) fprintf(stderr, "WOLFBOOT_SECTOR_SIZE cannot be 0\n"); exit(6); } + if (sec_sz > 0xFFFFU) { + fprintf(stderr, + "WOLFBOOT_SECTOR_SIZE (%" PRIu32 ") exceeds delta encoding limit (65535)\n", + sec_sz); + exit(6); + } if (sec_sz > (uint32_t)INT_MAX) { fprintf(stderr, "WOLFBOOT_SECTOR_SIZE (%" PRIu32 ") exceeds INT_MAX (%d)\n", sec_sz, INT_MAX); diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index 060f77bc97..d5dae83a2e 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include "delta.h" #define WC_RSA_BLINDING @@ -480,6 +483,36 @@ START_TEST(test_wb_patch_and_diff_multi_sector_images) } END_TEST +START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) +{ + const char *saved = getenv("WOLFBOOT_SECTOR_SIZE"); + char *saved_copy = saved ? strdup(saved) : NULL; + pid_t pid; + int status = 0; + + ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", "0x20000", 1), 0); + pid = fork(); + ck_assert_int_ne(pid, -1); + + if (pid == 0) { + (void)wb_diff_get_sector_size(); + _exit(0); + } + + ck_assert_int_eq(waitpid(pid, &status, 0), pid); + ck_assert_int_eq(WIFEXITED(status), 1); + ck_assert_int_eq(WEXITSTATUS(status), 6); + + if (saved_copy != NULL) { + ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", saved_copy, 1), 0); + free(saved_copy); + } + else { + ck_assert_int_eq(unsetenv("WOLFBOOT_SECTOR_SIZE"), 0); + } +} +END_TEST + START_TEST(test_wb_patch_and_diff_size_changing_update) { uint8_t src_a[2048]; @@ -539,6 +572,7 @@ Suite *patch_diff_suite(void) tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_completely_different_images); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_all_escape_images); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_multi_sector_images); + tcase_add_test(tc_wolfboot_delta, test_wb_diff_get_sector_size_rejects_values_above_16bit); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_size_changing_update); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_single_byte_difference); suite_add_tcase(s, tc_wolfboot_delta); From 657e3375aedf4de7c469d897404b4caf51af1867 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:14:46 +0200 Subject: [PATCH 20/41] Reject oversized cert-chain TLVs in sign tool F/2583 --- tools/keytools/sign.c | 9 ++++ tools/unit-tests/unit-sign-encrypted-output.c | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 8e1217b6e6..166ac25c5a 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -1450,6 +1450,15 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, cert_chain_sz = file_stat.st_size; + if (cert_chain_sz > (uint32_t)UINT16_MAX) { + printf("Error: Certificate chain too large for TLV encoding " + "(%u > %u)\n", + cert_chain_sz, (unsigned int)UINT16_MAX); + fclose(f); + f = NULL; + goto failure; + } + /* Verify that the chain will fit in our header */ if (header_idx + cert_chain_tlv_hdr_sz + cert_chain_sz > CMD.header_sz) { diff --git a/tools/unit-tests/unit-sign-encrypted-output.c b/tools/unit-tests/unit-sign-encrypted-output.c index 0741ef0ef5..5f26aa8db7 100644 --- a/tools/unit-tests/unit-sign-encrypted-output.c +++ b/tools/unit-tests/unit-sign-encrypted-output.c @@ -605,6 +605,50 @@ START_TEST(test_make_header_ex_keeps_boundary_header_for_sha384_sha3_hybrid_cert } END_TEST +START_TEST(test_make_header_ex_rejects_cert_chain_tlv_length_overflow) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + char cert_chain_path[PATH_MAX]; + uint8_t image_buf[] = { 0x41, 0x42, 0x43, 0x44 }; + uint8_t pubkey[] = { 0xA5 }; + uint8_t *cert_chain_buf = NULL; + const uint32_t cert_chain_len = 65536U; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + snprintf(cert_chain_path, sizeof(cert_chain_path), "%s/cert-chain.bin", + tempdir); + + cert_chain_buf = malloc(cert_chain_len); + ck_assert_ptr_nonnull(cert_chain_buf); + memset(cert_chain_buf, 0xC7, cert_chain_len); + + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + ck_assert_int_eq(write_file(cert_chain_path, cert_chain_buf, + cert_chain_len), 0); + + reset_cmd_defaults(); + CMD.cert_chain_file = cert_chain_path; + + reset_mocks(NULL, 0); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_ne(ret, 0); + + free(cert_chain_buf); + unlink(output_path); + unlink(cert_chain_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + Suite *wolfboot_suite(void) { Suite *s = suite_create("sign-encrypted-output"); @@ -620,6 +664,8 @@ Suite *wolfboot_suite(void) test_make_header_ex_roundtrip_finds_tlv_that_exactly_fills_header); tcase_add_test(tcase, test_make_header_ex_keeps_boundary_header_for_sha384_sha3_hybrid_cert_chain); + tcase_add_test(tcase, + test_make_header_ex_rejects_cert_chain_tlv_length_overflow); suite_add_tcase(s, tcase); return s; From 46645a0658b2a24ac5f6b996038a114a292d7c48 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:18:02 +0200 Subject: [PATCH 21/41] sign tool: encode header fields as little-endian F/2584 --- tools/keytools/sign.c | 89 +++++++++++++++---- tools/unit-tests/unit-sign-encrypted-output.c | 59 +++++++++++- 2 files changed, 125 insertions(+), 23 deletions(-) diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 166ac25c5a..df0170d2ac 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -211,14 +211,37 @@ static inline int fp_truncate(FILE *f, size_t len) #define ENC_MAX_KEY_SZ ENCRYPT_KEY_SIZE_AES256 /* 32 */ #define ENC_MAX_IV_SZ ENCRYPT_NONCE_SIZE_AES /* 16 */ +static void header_store_u16_le(uint8_t *dst, uint16_t val) +{ + dst[0] = (uint8_t)(val & 0xFFU); + dst[1] = (uint8_t)((val >> 8) & 0xFFU); +} + +static void header_store_u32_le(uint8_t *dst, uint32_t val) +{ + dst[0] = (uint8_t)(val & 0xFFU); + dst[1] = (uint8_t)((val >> 8) & 0xFFU); + dst[2] = (uint8_t)((val >> 16) & 0xFFU); + dst[3] = (uint8_t)((val >> 24) & 0xFFU); +} + +static void header_store_u64_le(uint8_t *dst, uint64_t val) +{ + uint32_t lo = (uint32_t)(val & 0xFFFFFFFFULL); + uint32_t hi = (uint32_t)(val >> 32); + + header_store_u32_le(dst, lo); + header_store_u32_le(dst + 4, hi); +} + static void header_append_u32(uint8_t* header, uint32_t* idx, uint32_t tmp32) { - memcpy(&header[*idx], &tmp32, sizeof(tmp32)); + header_store_u32_le(&header[*idx], tmp32); *idx += sizeof(tmp32); } static void header_append_u16(uint8_t* header, uint32_t* idx, uint16_t tmp16) { - memcpy(&header[*idx], &tmp16, sizeof(tmp16)); + header_store_u16_le(&header[*idx], tmp16); *idx += sizeof(tmp16); } @@ -244,6 +267,33 @@ static void header_append_tag(uint8_t* header, uint32_t* idx, uint16_t tag, *idx += len; } +static void header_append_tag_u16(uint8_t *header, uint32_t *idx, uint16_t tag, + uint16_t val) +{ + uint8_t tmp[sizeof(val)]; + + header_store_u16_le(tmp, val); + header_append_tag(header, idx, tag, sizeof(val), tmp); +} + +static void header_append_tag_u32(uint8_t *header, uint32_t *idx, uint16_t tag, + uint32_t val) +{ + uint8_t tmp[sizeof(val)]; + + header_store_u32_le(tmp, val); + header_append_tag(header, idx, tag, sizeof(val), tmp); +} + +static void header_append_tag_u64(uint8_t *header, uint32_t *idx, uint16_t tag, + uint64_t val) +{ + uint8_t tmp[sizeof(val)]; + + header_store_u64_le(tmp, val); + header_append_tag(header, idx, tag, sizeof(val), tmp); +} + #include "../lms/lms_common.h" #include "../xmss/xmss_common.h" @@ -1340,8 +1390,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Append Version field */ fw_version32 = strtol(CMD.fw_version, NULL, 10); - header_append_tag(header, &header_idx, HDR_VERSION, HDR_VERSION_LEN, - &fw_version32); + header_append_tag_u32(header, &header_idx, HDR_VERSION, fw_version32); /* Append pad bytes, so timestamp val field is 8-byte aligned */ ALIGN_8(header_idx); @@ -1349,8 +1398,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (!CMD.no_ts) { /* Append Timestamp field */ stat(image_file, &attrib); - header_append_tag(header, &header_idx, HDR_TIMESTAMP, HDR_TIMESTAMP_LEN, - &attrib.st_ctime); + header_append_tag_u64(header, &header_idx, HDR_TIMESTAMP, + (uint64_t)attrib.st_ctime); } /* Append Image type field */ @@ -1358,23 +1407,22 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, image_type |= CMD.partition_id; if (is_diff) image_type |= HDR_IMG_TYPE_DIFF; - header_append_tag(header, &header_idx, HDR_IMG_TYPE, HDR_IMG_TYPE_LEN, - &image_type); + header_append_tag_u16(header, &header_idx, HDR_IMG_TYPE, image_type); if (is_diff) { /* Append pad bytes, so fields are 4-byte aligned */ ALIGN_4(header_idx); - header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE, 4, - &delta_base_version); - header_append_tag(header, &header_idx, HDR_IMG_DELTA_SIZE, 4, - &patch_len); + header_append_tag_u32(header, &header_idx, HDR_IMG_DELTA_BASE, + delta_base_version); + header_append_tag_u32(header, &header_idx, HDR_IMG_DELTA_SIZE, + patch_len); /* Append pad bytes, so fields are 4-byte aligned */ ALIGN_4(header_idx); - header_append_tag(header, &header_idx, HDR_IMG_DELTA_INVERSE, 4, - &patch_inv_off); - header_append_tag(header, &header_idx, HDR_IMG_DELTA_INVERSE_SIZE, 4, - &patch_inv_len); + header_append_tag_u32(header, &header_idx, HDR_IMG_DELTA_INVERSE, + patch_inv_off); + header_append_tag_u32(header, &header_idx, HDR_IMG_DELTA_INVERSE_SIZE, + patch_inv_len); if (!CMD.no_base_sha) { /* Append pad bytes, so base hash is 8-byte aligned */ @@ -1535,7 +1583,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, wc_Sha256Final(&sha, second_buf); wc_Sha256Free(&sha); /* Add Secondary cipher to header */ - header_append_tag(header, &header_idx, HDR_SECONDARY_CIPHER, 2, &CMD.secondary_sign); + header_append_tag_u16(header, &header_idx, + HDR_SECONDARY_CIPHER, (uint16_t)CMD.secondary_sign); ALIGN_8(header_idx); /* Add Secondary Pubkey Hash to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_PUBKEY, digest_sz, second_buf); @@ -1612,7 +1661,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (ret == 0) { wc_Sha384Final(&sha, second_buf); /* Add Secondary cipher to header */ - header_append_tag(header, &header_idx, HDR_SECONDARY_CIPHER, 2, &CMD.secondary_sign); + header_append_tag_u16(header, &header_idx, + HDR_SECONDARY_CIPHER, (uint16_t)CMD.secondary_sign); /* Add Secondary Pubkey Hash to header */ header_append_tag(header, &header_idx, HDR_SECONDARY_PUBKEY, digest_sz, second_buf); DEBUG_PRINT("Secondary pubkey hash %d\n", digest_sz); @@ -1683,7 +1733,8 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, if (ret == 0) { ret = wc_Sha3_384_Final(&sha, second_buf); /* Add Secondary cipher to header */ - header_append_tag(header, &header_idx, HDR_SECONDARY_CIPHER, 2, &CMD.secondary_sign); + header_append_tag_u16(header, &header_idx, + HDR_SECONDARY_CIPHER, (uint16_t)CMD.secondary_sign); header_append_tag(header, &header_idx, HDR_SECONDARY_PUBKEY, digest_sz, second_buf); DEBUG_PRINT("Secondary pubkey hash %d\n", digest_sz); DEBUG_BUFFER(second_buf, digest_sz); diff --git a/tools/unit-tests/unit-sign-encrypted-output.c b/tools/unit-tests/unit-sign-encrypted-output.c index 5f26aa8db7..fdda2944cc 100644 --- a/tools/unit-tests/unit-sign-encrypted-output.c +++ b/tools/unit-tests/unit-sign-encrypted-output.c @@ -237,6 +237,20 @@ static void assert_header_bytes(const uint8_t *image, uint16_t tag, "Tag 0x%04x mismatch", tag); } +static void store_u16_le(uint8_t *dst, uint16_t val) +{ + dst[0] = (uint8_t)(val & 0xFFU); + dst[1] = (uint8_t)((val >> 8) & 0xFFU); +} + +static void store_u32_le(uint8_t *dst, uint32_t val) +{ + dst[0] = (uint8_t)(val & 0xFFU); + dst[1] = (uint8_t)((val >> 8) & 0xFFU); + dst[2] = (uint8_t)((val >> 16) & 0xFFU); + dst[3] = (uint8_t)((val >> 24) & 0xFFU); +} + static uint16_t find_exact_fill_custom_len(void) { uint16_t len; @@ -413,6 +427,39 @@ START_TEST(test_make_header_ex_grows_header_for_cert_chain_and_digest_tlvs) } END_TEST +START_TEST(test_header_append_helpers_emit_little_endian_bytes) +{ + uint8_t header[32]; + uint8_t value[] = { 0xAA, 0x55, 0x77 }; + uint32_t idx = 0; + + reset_cmd_defaults(); + CMD.header_sz = sizeof(header); + memset(header, 0x00, sizeof(header)); + + header_append_u32(header, &idx, 0x11223344U); + ck_assert_uint_eq(idx, 4); + ck_assert_uint_eq(header[0], 0x44); + ck_assert_uint_eq(header[1], 0x33); + ck_assert_uint_eq(header[2], 0x22); + ck_assert_uint_eq(header[3], 0x11); + + header_append_u16(header, &idx, 0x5566U); + ck_assert_uint_eq(idx, 6); + ck_assert_uint_eq(header[4], 0x66); + ck_assert_uint_eq(header[5], 0x55); + + header_append_tag(header, &idx, 0xABCDU, (uint16_t)sizeof(value), value); + ck_assert_uint_eq(idx, 13); + ck_assert_uint_eq(header[6], 0xCD); + ck_assert_uint_eq(header[7], 0xAB); + ck_assert_uint_eq(header[8], (uint8_t)sizeof(value)); + ck_assert_uint_eq(header[9], 0x00); + ck_assert_msg(memcmp(&header[10], value, sizeof(value)) == 0, + "header_append_tag payload mismatch"); +} +END_TEST + START_TEST(test_make_header_ex_roundtrip_custom_tlvs_via_wolfboot_parser) { char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; @@ -427,6 +474,8 @@ START_TEST(test_make_header_ex_roundtrip_custom_tlvs_via_wolfboot_parser) uint8_t tlv_three[] = { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28 }; uint16_t image_type; uint32_t version = 7; + uint8_t version_le[4]; + uint8_t image_type_le[2]; size_t output_len; int ret; @@ -460,10 +509,11 @@ START_TEST(test_make_header_ex_roundtrip_custom_tlvs_via_wolfboot_parser) ck_assert_int_eq(ret, 0); ck_assert_int_eq(read_file(output_path, &output_buf, &output_len), 0); ck_assert_uint_eq(output_len, CMD.header_sz + sizeof(image_buf)); - assert_header_bytes(output_buf, HDR_VERSION, (uint8_t *)&version, - sizeof(version)); - assert_header_bytes(output_buf, HDR_IMG_TYPE, (uint8_t *)&image_type, - sizeof(image_type)); + store_u32_le(version_le, version); + store_u16_le(image_type_le, image_type); + assert_header_bytes(output_buf, HDR_VERSION, version_le, sizeof(version_le)); + assert_header_bytes(output_buf, HDR_IMG_TYPE, image_type_le, + sizeof(image_type_le)); assert_header_bytes(output_buf, 0x30, tlv_one, sizeof(tlv_one)); assert_header_bytes(output_buf, 0x31, tlv_two, sizeof(tlv_two)); assert_header_bytes(output_buf, 0x32, tlv_three, sizeof(tlv_three)); @@ -658,6 +708,7 @@ Suite *wolfboot_suite(void) tcase_add_test(tcase, test_make_header_ex_fails_when_image_reopen_fails); tcase_add_test(tcase, test_make_header_ex_grows_header_for_cert_chain_and_digest_tlvs); + tcase_add_test(tcase, test_header_append_helpers_emit_little_endian_bytes); tcase_add_test(tcase, test_make_header_ex_roundtrip_custom_tlvs_via_wolfboot_parser); tcase_add_test(tcase, From cc7c3bbb68c41621cc30053df7f8d25e517b710f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:25:10 +0200 Subject: [PATCH 22/41] Reject oversized signature TLV lengths F/2585 --- tools/keytools/sign.c | 16 ++++++ tools/unit-tests/unit-sign-encrypted-output.c | 49 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index df0170d2ac..3551f4e874 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -1945,6 +1945,22 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, DEBUG_BUFFER(policy + sizeof(uint32_t), CMD.policy_sz); } + if (CMD.signature_sz > (uint32_t)UINT16_MAX) { + printf("Error: Signature too large for TLV encoding (%u > %u)\n", + CMD.signature_sz, (unsigned int)UINT16_MAX); + ret = -1; + goto failure; + } + + if (CMD.hybrid && + CMD.secondary_signature_sz > (uint32_t)UINT16_MAX) { + printf("Error: Secondary signature too large for TLV encoding " + "(%u > %u)\n", + CMD.secondary_signature_sz, (unsigned int)UINT16_MAX); + ret = -1; + goto failure; + } + /* Add signature to header */ ALIGN_8(header_idx); header_append_tag(header, &header_idx, HDR_SIGNATURE, CMD.signature_sz, diff --git a/tools/unit-tests/unit-sign-encrypted-output.c b/tools/unit-tests/unit-sign-encrypted-output.c index fdda2944cc..c18e7b247a 100644 --- a/tools/unit-tests/unit-sign-encrypted-output.c +++ b/tools/unit-tests/unit-sign-encrypted-output.c @@ -699,6 +699,53 @@ START_TEST(test_make_header_ex_rejects_cert_chain_tlv_length_overflow) } END_TEST +START_TEST(test_make_header_ex_rejects_signature_tlv_length_overflow) +{ + char tempdir[] = "/tmp/wolfboot-sign-XXXXXX"; + char image_path[PATH_MAX]; + char output_path[PATH_MAX]; + char signature_path[PATH_MAX]; + uint8_t image_buf[] = { 0x11, 0x22, 0x33, 0x44 }; + uint8_t pubkey[] = { 0xA5 }; + uint8_t *signature_buf = NULL; + const uint32_t signature_len = 65536U; + int ret; + + ck_assert_ptr_nonnull(mkdtemp(tempdir)); + + snprintf(image_path, sizeof(image_path), "%s/image.bin", tempdir); + snprintf(output_path, sizeof(output_path), "%s/output.bin", tempdir); + snprintf(signature_path, sizeof(signature_path), "%s/signature.bin", tempdir); + + signature_buf = malloc(signature_len); + ck_assert_ptr_nonnull(signature_buf); + memset(signature_buf, 0x5A, signature_len); + + ck_assert_int_eq(write_file(image_path, image_buf, sizeof(image_buf)), 0); + ck_assert_int_eq(write_file(signature_path, signature_buf, signature_len), 0); + + reset_cmd_defaults(); + CMD.sign = SIGN_RSA2048; + CMD.manual_sign = 1; + CMD.signature_file = signature_path; + CMD.signature_sz = signature_len; + /* Keep room for a large signature TLV to expose uint16_t truncation. */ + CMD.header_sz = 131072U; + + reset_mocks(NULL, 0); + ret = make_header_ex(0, pubkey, sizeof(pubkey), image_path, output_path, + 0, 0, 0, 0, NULL, 0, NULL, 0); + + ck_assert_int_ne(ret, 0); + + free(signature_buf); + unlink(output_path); + unlink(signature_path); + unlink(image_path); + rmdir(tempdir); +} +END_TEST + Suite *wolfboot_suite(void) { Suite *s = suite_create("sign-encrypted-output"); @@ -717,6 +764,8 @@ Suite *wolfboot_suite(void) test_make_header_ex_keeps_boundary_header_for_sha384_sha3_hybrid_cert_chain); tcase_add_test(tcase, test_make_header_ex_rejects_cert_chain_tlv_length_overflow); + tcase_add_test(tcase, + test_make_header_ex_rejects_signature_tlv_length_overflow); suite_add_tcase(s, tcase); return s; From af241649df1edef3b96cbcf68a0adfa09faa5328 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:26:42 +0200 Subject: [PATCH 23/41] Fix update sector flag index truncation F/2586 --- src/libwolfboot.c | 4 ++-- tools/unit-tests/unit-nvm.c | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index e5a138c6e7..f5161446d5 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -693,7 +693,7 @@ int RAMFUNCTION wolfBoot_set_update_sector_flag(uint16_t sector, uint32_t *magic; uint8_t *flags; uint8_t fl_value; - uint8_t pos = sector >> 1; + uint32_t pos = sector >> 1; magic = get_partition_magic(PART_UPDATE); if (*magic != wolfboot_magic_trail) @@ -748,7 +748,7 @@ int wolfBoot_get_update_sector_flag(uint16_t sector, uint8_t *flag) { uint32_t *magic; uint8_t *flags; - uint8_t pos = sector >> 1; + uint32_t pos = sector >> 1; magic = get_partition_magic(PART_UPDATE); if (*magic != WOLFBOOT_MAGIC_TRAIL) return -1; diff --git a/tools/unit-tests/unit-nvm.c b/tools/unit-tests/unit-nvm.c index aa021ba9b0..240edd4311 100644 --- a/tools/unit-tests/unit-nvm.c +++ b/tools/unit-tests/unit-nvm.c @@ -362,6 +362,43 @@ START_TEST(test_get_update_sector_flag_rejects_invalid_magic) } END_TEST +START_TEST(test_update_sector_flag_high_index_does_not_alias_low_index) +{ + int ret; + uint8_t st = 0; + + ret = mmap_file("/tmp/wolfboot-unit-high-index.bin", (void *)MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); +#ifdef FLAGS_HOME + ret = mmap_file("/tmp/wolfboot-unit-high-index-int.bin", + (void *)MOCK_ADDRESS_BOOT, WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); +#endif + ret = mmap_file("/tmp/wolfboot-unit-high-index-swap.bin", + (void *)MOCK_ADDRESS_SWAP, WOLFBOOT_SECTOR_SIZE, NULL); + ck_assert(ret >= 0); + + hal_flash_unlock(); + wolfBoot_erase_partition(PART_UPDATE); + + ret = wolfBoot_set_update_sector_flag(1, SECT_FLAG_SWAPPING); + ck_assert_int_eq(ret, 0); + ret = wolfBoot_set_update_sector_flag(512, SECT_FLAG_UPDATED); + ck_assert_int_eq(ret, 0); + + ret = wolfBoot_get_update_sector_flag(1, &st); + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(st, SECT_FLAG_SWAPPING); + + ret = wolfBoot_get_update_sector_flag(512, &st); + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(st, SECT_FLAG_UPDATED); + + hal_flash_lock(); +} +END_TEST + Suite *wolfboot_suite(void) { @@ -375,6 +412,8 @@ Suite *wolfboot_suite(void) test_partition_magic_write_stops_on_flash_write_error); tcase_add_test(nvm_select_fresh_sector, test_get_update_sector_flag_rejects_invalid_magic); + tcase_add_test(nvm_select_fresh_sector, + test_update_sector_flag_high_index_does_not_alias_low_index); suite_add_tcase(s, nvm_select_fresh_sector); return s; From 4c0f425a7a154cf677b1a6674028fbc74791f2f6 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:28:44 +0200 Subject: [PATCH 24/41] Fix WOLFBOOT_MAX_SPACE precedence and test F/2587 --- include/image.h | 2 +- tools/unit-tests/Makefile | 4 +++ tools/unit-tests/unit-max-space.c | 49 +++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tools/unit-tests/unit-max-space.c diff --git a/include/image.h b/include/image.h index 8ebb2145ce..51bc52d6ff 100644 --- a/include/image.h +++ b/include/image.h @@ -1395,7 +1395,7 @@ static inline int wb_flash_write_verify_word(struct wolfBoot_image *img, #ifndef EXT_ENCRYPTED #define WOLFBOOT_MAX_SPACE (WOLFBOOT_PARTITION_SIZE - \ (TRAILER_SKIP + sizeof(uint32_t) + \ - (WOLFBOOT_PARTITION_SIZE + 1 / (WOLFBOOT_SECTOR_SIZE * 8)))) + ((WOLFBOOT_PARTITION_SIZE + 1) / (WOLFBOOT_SECTOR_SIZE * 8)))) #else #define WOLFBOOT_MAX_SPACE (WOLFBOOT_PARTITION_SIZE - ENCRYPT_TMP_SECRET_OFFSET) #endif diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 16cb6179f7..23f2fcaf6b 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -45,6 +45,7 @@ endif TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128 \ unit-aes256 unit-chacha20 unit-pci unit-mock-state unit-sectorflags \ + unit-max-space \ unit-image unit-image-rsa unit-nvm unit-nvm-flagshome unit-enc-nvm \ unit-enc-nvm-flagshome unit-delta unit-update-flash unit-update-flash-delta \ unit-update-flash-self-update \ @@ -205,6 +206,9 @@ unit-store-sbrk: unit-store-sbrk.c ../../src/store_sbrk.c unit-string: ../../include/target.h unit-string.c gcc -o $@ $^ $(CFLAGS) -DDEBUG_UART -DPRINTF_ENABLED $(LDFLAGS) +unit-max-space: ../../include/target.h unit-max-space.c + gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) + unit-update-flash-self-update: ../../include/target.h unit-update-flash.c gcc -o $@ unit-update-flash.c ../../src/image.c \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \ diff --git a/tools/unit-tests/unit-max-space.c b/tools/unit-tests/unit-max-space.c new file mode 100644 index 0000000000..fb62568c1e --- /dev/null +++ b/tools/unit-tests/unit-max-space.c @@ -0,0 +1,49 @@ +/* unit-max-space.c + * + * Unit test for WOLFBOOT_MAX_SPACE macro arithmetic in image.h + */ + +#include +#include + +#define TRAILER_SKIP 0 +#include "image.h" + +START_TEST(test_wolfboot_max_space_formula) +{ + uint32_t expected = (uint32_t)(WOLFBOOT_PARTITION_SIZE - + (TRAILER_SKIP + sizeof(uint32_t) + + ((WOLFBOOT_PARTITION_SIZE + 1U) / (WOLFBOOT_SECTOR_SIZE * 8U)))); + uint32_t actual = (uint32_t)WOLFBOOT_MAX_SPACE; + + ck_assert_uint_eq(actual, expected); +} +END_TEST + +Suite *wolfboot_suite(void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create("WOLFBOOT_MAX_SPACE"); + tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_wolfboot_max_space_formula); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = wolfboot_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? 0 : 1; +} From dd6444bd4cb09f86e680a34b76dafe03518b1cd1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:30:15 +0200 Subject: [PATCH 25/41] x86: zero ATA unlock secret on all sata_unlock_disk exits F/2588 --- src/x86/ahci.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/x86/ahci.c b/src/x86/ahci.c index 45d97c4207..43c9cc75ad 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -416,36 +416,36 @@ int sata_unlock_disk(int drv, int freeze) int secret_size = ATA_UNLOCK_DISK_KEY_SZ; uint8_t secret[ATA_UNLOCK_DISK_KEY_SZ]; enum ata_security_state ata_st; - int r; + int r = -1; #ifdef WOLFBOOT_ATA_DISABLE_USER_PASSWORD sata_disable_password(0); #endif r = sata_get_unlock_secret(secret, &secret_size); if (r != 0) - return r; + goto cleanup; ata_st = ata_security_get_state(drv); wolfBoot_printf("ATA: Security state SEC%d\r\n", ata_st); #if defined(TARGET_x86_fsp_qemu) if (ata_st == ATA_SEC0) - return 0; + goto cleanup; #endif if (ata_st == ATA_SEC1) { AHCI_DEBUG_PRINTF("ATA: calling set passphrase\r\n", r); r = ata_security_set_password(drv, 0, (char*)secret); if (r != 0) - return -1; + goto error; AHCI_DEBUG_PRINTF("ATA: calling freeze lock\r\n", r); if (freeze) { r = ata_security_freeze_lock(drv); AHCI_DEBUG_PRINTF("ATA security freeze lock: returned %d\r\n", r); if (r != 0) - return -1; + goto error; } r = ata_identify_device(drv); AHCI_DEBUG_PRINTF("ATA identify: returned %d\r\n", r); if (r != 0) - return -1; + goto error; ata_st = ata_security_get_state(drv); wolfBoot_printf("ATA: State SEC%d\r\n", ata_st); } @@ -454,11 +454,11 @@ int sata_unlock_disk(int drv, int freeze) r = ata_security_unlock_device(drv, (char*)secret, 0); AHCI_DEBUG_PRINTF("ATA device unlock: returned %d\r\n", r); if (r != 0) - return -1; + goto error; r = ata_identify_device(drv); AHCI_DEBUG_PRINTF("ATA identify: returned %d\r\n", r); if (r != 0) - return -1; + goto error; ata_st = ata_security_get_state(drv); if (ata_st == ATA_SEC5) { if (freeze) { @@ -467,14 +467,14 @@ int sata_unlock_disk(int drv, int freeze) AHCI_DEBUG_PRINTF("ATA security freeze lock: returned %d\r\n", r); if (r != 0) - return -1; + goto error; } else { AHCI_DEBUG_PRINTF("ATA security freeze skipped\r\n"); } r = ata_identify_device(drv); AHCI_DEBUG_PRINTF("ATA identify: returned %d\r\n", r); if (r != 0) - return -1; + goto error; } } ata_st = ata_security_get_state(drv); @@ -484,8 +484,13 @@ int sata_unlock_disk(int drv, int freeze) panic(); } AHCI_DEBUG_PRINTF("ATA: Security enabled. State SEC%d\r\n", ata_st); - - return 0; + r = 0; + goto cleanup; +error: + r = -1; +cleanup: + TPM2_ForceZero(secret, sizeof(secret)); + return r; } #endif /* WOLFBOOT_ATA_DISK_LOCK */ From 76f0b3cba9655cc1f5f73387b367540152e59a2d Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:32:45 +0200 Subject: [PATCH 26/41] keygen_xmss: zeroize XmssKey after free F/2590 --- tools/keytools/keygen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 7e4515cad5..1b99309f79 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -1048,6 +1048,7 @@ static void keygen_xmss(const char *priv_fname, uint32_t id_mask) keystore_add(AUTH_KEY_XMSS, xmss_pub, KEYSTORE_PUBKEY_SIZE_XMSS, priv_fname, id_mask); wc_XmssKey_Free(&key); + wc_ForceZero(&key, sizeof(key)); } From 84562a41e3e1c5e19672fc52e6ee02e6b60203d0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:33:50 +0200 Subject: [PATCH 27/41] dice: zero IAK stack key on all exits F/2591 --- src/dice/dice.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dice/dice.c b/src/dice/dice.c index ecdaaf54b7..5bb499b046 100644 --- a/src/dice/dice.c +++ b/src/dice/dice.c @@ -660,18 +660,23 @@ static int wolfboot_attest_get_private_key(ecc_key *key, { uint8_t priv[WOLFBOOT_DICE_KEY_LEN]; size_t priv_len = sizeof(priv); + int ret = -1; if (hal_attestation_get_iak_private_key(priv, &priv_len) != 0) { - return -1; + goto cleanup; } if (priv_len != WOLFBOOT_DICE_KEY_LEN) { - return -1; + goto cleanup; } if (wc_ecc_import_private_key_ex(priv, (word32)priv_len, NULL, 0, key, ECC_SECP256R1) != 0) { - return -1; + goto cleanup; } - return 0; + ret = 0; + +cleanup: + wc_ForceZero(priv, sizeof(priv)); + return ret; } #else if (hal_uds_derive_key(uds, uds_len) != 0) { From 8dbc7d6a8e778c872edfba06a3aa9d0d630332e9 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:35:24 +0200 Subject: [PATCH 28/41] dice: scrub non-IAK UDS buffer F/2592 --- src/dice/dice.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dice/dice.c b/src/dice/dice.c index 5bb499b046..14d8c7126b 100644 --- a/src/dice/dice.c +++ b/src/dice/dice.c @@ -679,10 +679,13 @@ static int wolfboot_attest_get_private_key(ecc_key *key, return ret; } #else - if (hal_uds_derive_key(uds, uds_len) != 0) { - return -1; + int ret = -1; + + if (hal_uds_derive_key(uds, uds_len) == 0) { + ret = wolfboot_dice_derive_attestation_key(key, uds, uds_len, claims); } - return wolfboot_dice_derive_attestation_key(key, uds, uds_len, claims); + wc_ForceZero(uds, sizeof(uds)); + return ret; #endif } From da2c3dd8d5c4008c4f5f6b26054ba521dd6c64bb Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:37:25 +0200 Subject: [PATCH 29/41] dice: zero ECC signing key context on cleanup F/2593 --- src/dice/dice.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/dice/dice.c b/src/dice/dice.c index 14d8c7126b..e4323d87e7 100644 --- a/src/dice/dice.c +++ b/src/dice/dice.c @@ -809,7 +809,10 @@ static int wolfboot_dice_sign_tbs(const uint8_t *tbs, { ecc_key key; WC_RNG rng; - int ret; + int ret = WOLFBOOT_DICE_ERR_CRYPTO; + int wc_ret; + int key_inited = 0; + int rng_inited = 0; uint8_t hash[SHA256_DIGEST_SIZE]; uint8_t der_sig[128]; word32 der_sig_len = sizeof(der_sig); @@ -823,16 +826,18 @@ static int wolfboot_dice_sign_tbs(const uint8_t *tbs, } wc_ecc_init(&key); + key_inited = 1; if (wolfboot_attest_get_private_key(&key, claims) != 0) { - wc_ecc_free(&key); - return WOLFBOOT_DICE_ERR_HW; + ret = WOLFBOOT_DICE_ERR_HW; + goto cleanup; } (void)wc_ecc_set_deterministic(&key, 1); if (wc_InitRng(&rng) != 0) { - wc_ecc_free(&key); - return WOLFBOOT_DICE_ERR_HW; + ret = WOLFBOOT_DICE_ERR_HW; + goto cleanup; } + rng_inited = 1; { wc_Sha256 sha; @@ -841,26 +846,35 @@ static int wolfboot_dice_sign_tbs(const uint8_t *tbs, wc_Sha256Final(&sha, hash); } - ret = wc_ecc_sign_hash(hash, sizeof(hash), der_sig, &der_sig_len, &rng, &key); - wc_FreeRng(&rng); - if (ret != 0) { - wc_ecc_free(&key); - return WOLFBOOT_DICE_ERR_CRYPTO; + wc_ret = wc_ecc_sign_hash(hash, sizeof(hash), der_sig, &der_sig_len, &rng, &key); + if (wc_ret != 0) { + ret = WOLFBOOT_DICE_ERR_CRYPTO; + goto cleanup; } - ret = wc_ecc_sig_to_rs(der_sig, der_sig_len, r, &r_len, s, &s_len); - if (ret != 0 || r_len > sizeof(r) || s_len > sizeof(s)) { - wc_ecc_free(&key); - return WOLFBOOT_DICE_ERR_CRYPTO; + wc_ret = wc_ecc_sig_to_rs(der_sig, der_sig_len, r, &r_len, s, &s_len); + if (wc_ret != 0 || r_len > sizeof(r) || s_len > sizeof(s)) { + ret = WOLFBOOT_DICE_ERR_CRYPTO; + goto cleanup; } XMEMSET(sig, 0, WOLFBOOT_DICE_SIG_LEN); XMEMCPY(sig + (sizeof(r) - r_len), r, r_len); XMEMCPY(sig + sizeof(r) + (sizeof(s) - s_len), s, s_len); *sig_len = WOLFBOOT_DICE_SIG_LEN; + ret = WOLFBOOT_DICE_SUCCESS; - wc_ecc_free(&key); - return WOLFBOOT_DICE_SUCCESS; +cleanup: + if (rng_inited) { + wc_FreeRng(&rng); + } + if (key_inited) { + wc_ecc_free(&key); + wc_ForceZero(&key, sizeof(key)); + } + wc_ForceZero(hash, sizeof(hash)); + wc_ForceZero(der_sig, sizeof(der_sig)); + return ret; } static int wolfboot_dice_build_token(uint8_t *token_buf, From 25668dd39b4966052e994fcb3c4cfad57a2796c9 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:38:51 +0200 Subject: [PATCH 30/41] zeroize OTP UDS in stm32h5 derive path F/2594 --- hal/stm32h5.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hal/stm32h5.c b/hal/stm32h5.c index a20c540826..f0e1bd334d 100644 --- a/hal/stm32h5.c +++ b/hal/stm32h5.c @@ -42,6 +42,7 @@ #elif defined(WOLFBOOT_HASH_SHA3_384) #include #endif +#include #endif #define PLL_SRC_HSE 1 @@ -272,9 +273,11 @@ int hal_uds_derive_key(uint8_t *out, size_t out_len) copy_len = out_len; } memcpy(out, uds, copy_len); + wc_ForceZero(uds, sizeof(uds)); return 0; } } + wc_ForceZero(uds, sizeof(uds)); #endif #ifdef WOLFBOOT_UDS_UID_FALLBACK_FORTEST From e48c1eb23797c27b55913c270553e33e0e16b72c Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 14:39:48 +0200 Subject: [PATCH 31/41] zero entropy buffer in sata_get_random_base64 F/2595 --- src/x86/ahci.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/x86/ahci.c b/src/x86/ahci.c index 43c9cc75ad..fb2c38142a 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -257,15 +257,15 @@ static int sata_get_random_base64(uint8_t *out, int *out_size) { uint8_t rand[ATA_SECRET_RANDOM_BYTES]; word32 base_64_len; - int ret; + int ret = -1; ret = wolfBoot_get_random(rand, ATA_SECRET_RANDOM_BYTES); if (ret != 0) - return ret; + goto cleanup; base_64_len = *out_size; ret = Base64_Encode_NoNl(rand, ATA_SECRET_RANDOM_BYTES, out, &base_64_len); if (ret != 0) - return ret; + goto cleanup; /* double check we have a NULL-terminated string */ if ((int)base_64_len < *out_size) { @@ -275,7 +275,11 @@ static int sata_get_random_base64(uint8_t *out, int *out_size) out[base_64_len-1] = '\0'; } *out_size = (int)base_64_len; - return 0; + ret = 0; + +cleanup: + TPM2_ForceZero(rand, sizeof(rand)); + return ret; } static int sata_create_and_seal_unlock_secret(const uint8_t *pubkey_hint, From faebb6211541d55008d54469ba1ed336650caffa Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 16:24:03 +0200 Subject: [PATCH 32/41] Fixed test regressions --- .gitignore | 5 ++++- src/tpm.c | 5 +++-- src/update_flash.c | 16 ++++++++++++++-- tools/unit-tests/Makefile | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index eccdcaee48..71dcbc5cba 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,7 @@ include/target.h .wolfboot-offset .wolfboot-partition-size .bootloader-partition-size +NVChip MPLabX/wolfBoot-SAME51.X/.generated_files/ test-dummy-ca/** @@ -183,6 +184,9 @@ tools/unit-tests/unit-update-flash-delta tools/unit-tests/unit-update-flash-self-update tools/unit-tests/unit-loader-tpm-init tools/unit-tests/unit-update-ram-nofixed +tools/unit-tests/unit-max-space +tools/unit-tests/unit-sdhci-disk-unaligned + @@ -376,4 +380,3 @@ system-default.dtb test_output/ sdcard.img - diff --git a/src/tpm.c b/src/tpm.c index d5d6f8136e..a5777337a2 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -355,13 +355,14 @@ int wolfBoot_tpm2_extend(uint8_t pcrIndex, uint8_t* hash, int line) TPM2_GetHashDigestSize(WOLFBOOT_TPM_PCR_ALG)); #ifdef WOLFBOOT_DEBUG_TPM if (rc == 0) { + int read_rc; wolfBoot_printf("Measured boot: Index %d, Line %d\n", pcrIndex, line); - rc = wolfTPM2_ReadPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG, + read_rc = wolfTPM2_ReadPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG, digest, &digestSz); wolfBoot_printf("PCR %d: Res %d, Digest Sz %d\n", - pcrIndex, rc, digestSz); + pcrIndex, read_rc, digestSz); wolfBoot_print_bin(digest, digestSz); } else { diff --git a/src/update_flash.c b/src/update_flash.c index fd60efae82..8aafbd24bb 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -567,6 +567,18 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) # define DELTA_BLOCK_SIZE 1024 #endif +static inline uint32_t wb_im2n32(uint32_t val) +{ +#ifdef BIG_ENDIAN_ORDER + return val; +#else + return (((val & 0x000000FFU) << 24) | + ((val & 0x0000FF00U) << 8) | + ((val & 0x00FF0000U) >> 8) | + ((val & 0xFF000000U) >> 24)); +#endif +} + static int wolfBoot_delta_update(struct wolfBoot_image *boot, struct wolfBoot_image *update, struct wolfBoot_image *swap, int inverse, int resume) @@ -619,9 +631,9 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, &delta_base_hash, &delta_base_hash_sz) < 0) { return -1; } - delta_img_size = im2n(*img_size); + delta_img_size = wb_im2n32(*img_size); if (inverse) - delta_img_offset = im2n(*img_offset); + delta_img_offset = wb_im2n32(*img_offset); cur_v = wolfBoot_current_firmware_version(); upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 23f2fcaf6b..0875ad64f1 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -240,7 +240,7 @@ unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c -Wl,--gc-sections unit-loader-tpm-init: ../../include/target.h unit-loader-tpm-init.c - gcc -o $@ $^ $(CFLAGS) -DWOLFBOOT_LOADER_MAIN -DWOLFBOOT_TPM \ + gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_LOADER_MAIN -DWOLFBOOT_TPM \ -DWOLFBOOT_HOOK_PANIC -DWOLFBOOT_SIGN_ECC256 \ -DWOLFBOOT_HASH_SHA256 -ffunction-sections -fdata-sections \ $(LDFLAGS) -Wl,--gc-sections From 89760785921a6f38e0428758b438a68eeecfb73f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 16:36:04 +0200 Subject: [PATCH 33/41] Adjusted footprint limits --- tools/test.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/test.mk b/tools/test.mk index fdc19c01d1..820edb93ef 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -1164,7 +1164,7 @@ test-size-all: make clean make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15290 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED448 LIMIT=13864 NO_ARM_ASM=1 + make test-size SIGN=ED448 LIMIT=13952 NO_ARM_ASM=1 make keysclean make test-size SIGN=RSA3072 LIMIT=12056 NO_ARM_ASM=1 make clean @@ -1179,5 +1179,5 @@ test-size-all: LIMIT=8658 NO_ARM_ASM=1 make keysclean make clean - make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19482 \ + make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19800 \ IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192 From 9ac6b1f485630cca4180fde72db7e373641b16a6 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 16:47:11 +0200 Subject: [PATCH 34/41] Fixed more regressions --- include/target.h.in | 35 ++++++++++++++++++----------------- src/update_flash.c | 4 ++-- tools/keytools/otp/Makefile | 1 + tools/unit-tests/Makefile | 1 + 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/include/target.h.in b/include/target.h.in index 6d1d273dd3..581417a9a5 100644 --- a/include/target.h.in +++ b/include/target.h.in @@ -110,37 +110,38 @@ #define WOLFBOOT_DTS_BOOT_ADDRESS @WOLFBOOT_DTS_BOOT_ADDRESS@ #define WOLFBOOT_DTS_UPDATE_ADDRESS @WOLFBOOT_DTS_UPDATE_ADDRESS@ -#if !defined(WOLFBOOT_PART_USE_ARCH_OFFSET) && !defined(PULL_LINKER_DEFINES) +#if defined(WOLFBOOT_FIXED_PARTITIONS) && \ + !defined(WOLFBOOT_PART_USE_ARCH_OFFSET) && !defined(PULL_LINKER_DEFINES) /* * Only compare partitions that share the same internal flash address * space. External partitions and runtime/linker-provided addresses are * validated elsewhere. */ #if !defined(PART_BOOT_EXT) && !defined(PART_UPDATE_EXT) && \ - (WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0) && \ - ((WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ - WOLFBOOT_PARTITION_UPDATE_ADDRESS) && \ - (WOLFBOOT_PARTITION_BOOT_ADDRESS < \ - (WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) + ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0) != 0) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + 0 + WOLFBOOT_PARTITION_SIZE + 0) > \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0)) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + 0) < \ + (WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0 + WOLFBOOT_PARTITION_SIZE + 0)) #error "Boot and update partitions overlap" #endif #if !defined(PART_BOOT_EXT) && !defined(PART_SWAP_EXT) && \ - (WOLFBOOT_PARTITION_SWAP_ADDRESS != 0) && \ - ((WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ - WOLFBOOT_PARTITION_SWAP_ADDRESS) && \ - (WOLFBOOT_PARTITION_BOOT_ADDRESS < \ - (WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + ((WOLFBOOT_PARTITION_SWAP_ADDRESS + 0) != 0) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + 0 + WOLFBOOT_PARTITION_SIZE + 0) > \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + 0)) && \ + ((WOLFBOOT_PARTITION_BOOT_ADDRESS + 0) < \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + 0 + WOLFBOOT_SECTOR_SIZE)) #error "Boot and swap partitions overlap" #endif #if !defined(PART_UPDATE_EXT) && !defined(PART_SWAP_EXT) && \ - (WOLFBOOT_PARTITION_UPDATE_ADDRESS != 0) && \ - (WOLFBOOT_PARTITION_SWAP_ADDRESS != 0) && \ - ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) > \ - WOLFBOOT_PARTITION_SWAP_ADDRESS) && \ - (WOLFBOOT_PARTITION_UPDATE_ADDRESS < \ - (WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) + ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0) != 0) && \ + ((WOLFBOOT_PARTITION_SWAP_ADDRESS + 0) != 0) && \ + ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0 + WOLFBOOT_PARTITION_SIZE + 0) > \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + 0)) && \ + ((WOLFBOOT_PARTITION_UPDATE_ADDRESS + 0) < \ + (WOLFBOOT_PARTITION_SWAP_ADDRESS + 0 + WOLFBOOT_SECTOR_SIZE)) #error "Update and swap partitions overlap" #endif #endif diff --git a/src/update_flash.c b/src/update_flash.c index 8aafbd24bb..da5d0cde18 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -570,12 +570,12 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) static inline uint32_t wb_im2n32(uint32_t val) { #ifdef BIG_ENDIAN_ORDER - return val; -#else return (((val & 0x000000FFU) << 24) | ((val & 0x0000FF00U) << 8) | ((val & 0x00FF0000U) >> 8) | ((val & 0xFF000000U) >> 24)); +#else + return val; #endif } diff --git a/tools/keytools/otp/Makefile b/tools/keytools/otp/Makefile index 7ad1b8240f..6f9d8f483c 100644 --- a/tools/keytools/otp/Makefile +++ b/tools/keytools/otp/Makefile @@ -19,6 +19,7 @@ CFLAGS+=-I. -I../../../ -I../../../include -I$(WOLFBOOT_LIB_WOLFSSL) \ CFLAGS+=-I./wcs CFLAGS+=-DFLASH_OTP_KEYSTORE -D__FLASH_OTP_PRIMER -DWOLFSSL_USER_SETTINGS -DWOLFCRYPT_SECURE_MODE PRI_KS_OBJS+=startup.o otp-keystore-primer.o ../../../src/keystore.o +PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.o ifeq ($(HASH),SHA256) PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 0875ad64f1..63b4334a14 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -241,6 +241,7 @@ unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c unit-loader-tpm-init: ../../include/target.h unit-loader-tpm-init.c gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_LOADER_MAIN -DWOLFBOOT_TPM \ + -DWOLFTPM_USER_SETTINGS \ -DWOLFBOOT_HOOK_PANIC -DWOLFBOOT_SIGN_ECC256 \ -DWOLFBOOT_HASH_SHA256 -ffunction-sections -fdata-sections \ $(LDFLAGS) -Wl,--gc-sections From b00746310330dd6897f4dd0bcbd56c719942a55f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 17:11:15 +0200 Subject: [PATCH 35/41] Addressed copilot comments --- src/update_flash.c | 6 +++--- src/x86/ahci.c | 4 +++- tools/unit-tests/unit-delta.c | 7 +++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/update_flash.c b/src/update_flash.c index da5d0cde18..6768c65411 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -567,7 +567,7 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) # define DELTA_BLOCK_SIZE 1024 #endif -static inline uint32_t wb_im2n32(uint32_t val) +static inline uint32_t im2n(uint32_t val) { #ifdef BIG_ENDIAN_ORDER return (((val & 0x000000FFU) << 24) | @@ -631,9 +631,9 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, &delta_base_hash, &delta_base_hash_sz) < 0) { return -1; } - delta_img_size = wb_im2n32(*img_size); + delta_img_size = im2n(*img_size); if (inverse) - delta_img_offset = wb_im2n32(*img_offset); + delta_img_offset = im2n(*img_offset); cur_v = wolfBoot_current_firmware_version(); upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); diff --git a/src/x86/ahci.c b/src/x86/ahci.c index fb2c38142a..d5be05fd8a 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -431,8 +431,10 @@ int sata_unlock_disk(int drv, int freeze) ata_st = ata_security_get_state(drv); wolfBoot_printf("ATA: Security state SEC%d\r\n", ata_st); #if defined(TARGET_x86_fsp_qemu) - if (ata_st == ATA_SEC0) + if (ata_st == ATA_SEC0) { + r = 0; goto cleanup; + } #endif if (ata_st == ATA_SEC1) { AHCI_DEBUG_PRINTF("ATA: calling set passphrase\r\n", r); diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index d5dae83a2e..3f04fbb22f 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -25,9 +25,12 @@ #include #include #include +#if defined(__unix__) || defined(__APPLE__) #include #include #include +#define HAVE_POSIX_FORK 1 +#endif #include "delta.h" #define WC_RSA_BLINDING @@ -485,6 +488,7 @@ END_TEST START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) { +#if HAVE_POSIX_FORK const char *saved = getenv("WOLFBOOT_SECTOR_SIZE"); char *saved_copy = saved ? strdup(saved) : NULL; pid_t pid; @@ -510,6 +514,9 @@ START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) else { ck_assert_int_eq(unsetenv("WOLFBOOT_SECTOR_SIZE"), 0); } +#else + ck_assert_int_eq(1, 1); +#endif } END_TEST From 00342ac584f99a8b772162e3eb5c585f87973331 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 17:25:18 +0200 Subject: [PATCH 36/41] Addressed more copilot's comments --- tools/keytools/sign.c | 50 ++++++++++++++++++++++++----------- tools/unit-tests/unit-delta.c | 2 +- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index 3551f4e874..9edd4f1ad3 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -1338,22 +1338,32 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, /* Get the file size */ if (stat(CMD.cert_chain_file, &file_stat) == 0) { - const uint32_t required_space = header_required_size(is_diff, - (uint32_t)file_stat.st_size, secondary_key_sz); - - /* If the current header size is too small, increase it */ - if (CMD.header_sz < required_space) { - /* Round up to nearest power of 2 that can hold the chain */ - const uint32_t min_header_size = 256; - uint32_t new_size = min_header_size; - while (new_size < required_space) { - new_size *= 2; - } + off_t chain_file_sz = file_stat.st_size; + uint32_t required_space; + + if ((chain_file_sz < 0) || + ((uintmax_t)chain_file_sz > (uintmax_t)UINT32_MAX)) { + printf("Warning: certificate chain file size is invalid (%jd)\n", + (intmax_t)chain_file_sz); + } + else { + required_space = header_required_size(is_diff, + (uint32_t)chain_file_sz, secondary_key_sz); + + /* If the current header size is too small, increase it */ + if (CMD.header_sz < required_space) { + /* Round up to nearest power of 2 that can hold the chain */ + const uint32_t min_header_size = 256; + uint32_t new_size = min_header_size; + while (new_size < required_space) { + new_size *= 2; + } - printf("Increasing header size from %u to %u bytes to fit " - "certificate chain\n", - CMD.header_sz, new_size); - CMD.header_sz = new_size; + printf("Increasing header size from %u to %u bytes to fit " + "certificate chain\n", + CMD.header_sz, new_size); + CMD.header_sz = new_size; + } } } else { @@ -1496,7 +1506,15 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz, goto failure; } - cert_chain_sz = file_stat.st_size; + if ((file_stat.st_size < 0) || + ((uintmax_t)file_stat.st_size > (uintmax_t)UINT32_MAX)) { + printf("Error: Invalid certificate chain file size (%jd)\n", + (intmax_t)file_stat.st_size); + fclose(f); + f = NULL; + goto failure; + } + cert_chain_sz = (uint32_t)file_stat.st_size; if (cert_chain_sz > (uint32_t)UINT16_MAX) { printf("Error: Certificate chain too large for TLV encoding " diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index 3f04fbb22f..d512687a99 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -504,7 +504,7 @@ START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) } ck_assert_int_eq(waitpid(pid, &status, 0), pid); - ck_assert_int_eq(WIFEXITED(status), 1); + ck_assert(WIFEXITED(status)); ck_assert_int_eq(WEXITSTATUS(status), 6); if (saved_copy != NULL) { From a64fa8fbcfd8a23e73315f037a546f6d0ad52535 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 17:59:30 +0200 Subject: [PATCH 37/41] Addressed Fenrir's review comments + fixed more regressions --- hal/stm32h5.c | 13 ++++++++--- src/delta.c | 6 ----- src/dice/dice.c | 29 +++++++++++++++--------- src/tpm.c | 8 +++---- src/update_flash.c | 6 ++--- tools/keytools/otp/Makefile | 1 - tools/keytools/otp/target.ld | 1 + tools/unit-tests/unit-update-flash.c | 34 ++++++++++++++++++++++++++++ 8 files changed, 70 insertions(+), 28 deletions(-) diff --git a/hal/stm32h5.c b/hal/stm32h5.c index f0e1bd334d..6ab0da1dab 100644 --- a/hal/stm32h5.c +++ b/hal/stm32h5.c @@ -42,7 +42,6 @@ #elif defined(WOLFBOOT_HASH_SHA3_384) #include #endif -#include #endif #define PLL_SRC_HSE 1 @@ -247,6 +246,14 @@ static int buffer_is_all_value(const uint8_t *buf, size_t len, uint8_t value) return 1; } +static NOINLINEFUNCTION void hal_secret_zeroize(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + while (len-- > 0U) { + *p++ = 0U; + } +} + int hal_uds_derive_key(uint8_t *out, size_t out_len) { #if defined(FLASH_OTP_KEYSTORE) @@ -273,11 +280,11 @@ int hal_uds_derive_key(uint8_t *out, size_t out_len) copy_len = out_len; } memcpy(out, uds, copy_len); - wc_ForceZero(uds, sizeof(uds)); + hal_secret_zeroize(uds, sizeof(uds)); return 0; } } - wc_ForceZero(uds, sizeof(uds)); + hal_secret_zeroize(uds, sizeof(uds)); #endif #ifdef WOLFBOOT_UDS_UID_FALLBACK_FORTEST diff --git a/src/delta.c b/src/delta.c index 145299fd5f..384c4fb4fe 100644 --- a/src/delta.c +++ b/src/delta.c @@ -185,7 +185,6 @@ int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len) #include #include #include -#include /* INT_MAX */ #include /* PRIu32 */ static uint32_t wolfboot_sector_size = 0; @@ -239,11 +238,6 @@ int wb_diff_get_sector_size(void) sec_sz); exit(6); } - if (sec_sz > (uint32_t)INT_MAX) { - fprintf(stderr, "WOLFBOOT_SECTOR_SIZE (%" PRIu32 ") exceeds INT_MAX (%d)\n", - sec_sz, INT_MAX); - exit(6); - } return (int)sec_sz; } diff --git a/src/dice/dice.c b/src/dice/dice.c index e4323d87e7..9272a5f2d1 100644 --- a/src/dice/dice.c +++ b/src/dice/dice.c @@ -35,7 +35,6 @@ #include #include #include -#include #if defined(WOLFBOOT_HASH_SHA384) #include @@ -68,6 +67,14 @@ #define WOLFBOOT_DICE_ERR_HW -3 #define WOLFBOOT_DICE_ERR_CRYPTO -4 +static NOINLINEFUNCTION void wolfboot_dice_zeroize(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + while (len-- > 0U) { + *p++ = 0U; + } +} + #define COSE_LABEL_ALG 1 #define COSE_ALG_ES256 (-7) @@ -621,7 +628,7 @@ static int wolfboot_dice_derive_attestation_key(ecc_key *key, goto cleanup; } /* CDI is no longer needed once the seed has been derived. */ - wc_ForceZero(cdi, sizeof(cdi)); + wolfboot_dice_zeroize(cdi, sizeof(cdi)); if (wolfboot_dice_hkdf(seed, sizeof(seed), (const uint8_t *)"WOLFBOOT-IAK", 12, @@ -630,7 +637,7 @@ static int wolfboot_dice_derive_attestation_key(ecc_key *key, goto cleanup; } /* Seed is no longer needed once the private key material is derived. */ - wc_ForceZero(seed, sizeof(seed)); + wolfboot_dice_zeroize(seed, sizeof(seed)); if (wolfboot_dice_fixup_priv(priv, sizeof(priv)) != 0) { goto cleanup; @@ -644,9 +651,9 @@ static int wolfboot_dice_derive_attestation_key(ecc_key *key, ret = 0; cleanup: - wc_ForceZero(priv, sizeof(priv)); - wc_ForceZero(seed, sizeof(seed)); - wc_ForceZero(cdi, sizeof(cdi)); + wolfboot_dice_zeroize(priv, sizeof(priv)); + wolfboot_dice_zeroize(seed, sizeof(seed)); + wolfboot_dice_zeroize(cdi, sizeof(cdi)); return ret; } @@ -675,7 +682,7 @@ static int wolfboot_attest_get_private_key(ecc_key *key, ret = 0; cleanup: - wc_ForceZero(priv, sizeof(priv)); + wolfboot_dice_zeroize(priv, sizeof(priv)); return ret; } #else @@ -684,7 +691,7 @@ static int wolfboot_attest_get_private_key(ecc_key *key, if (hal_uds_derive_key(uds, uds_len) == 0) { ret = wolfboot_dice_derive_attestation_key(key, uds, uds_len, claims); } - wc_ForceZero(uds, sizeof(uds)); + wolfboot_dice_zeroize(uds, sizeof(uds)); return ret; #endif } @@ -870,10 +877,10 @@ static int wolfboot_dice_sign_tbs(const uint8_t *tbs, } if (key_inited) { wc_ecc_free(&key); - wc_ForceZero(&key, sizeof(key)); + wolfboot_dice_zeroize(&key, sizeof(key)); } - wc_ForceZero(hash, sizeof(hash)); - wc_ForceZero(der_sig, sizeof(der_sig)); + wolfboot_dice_zeroize(hash, sizeof(hash)); + wolfboot_dice_zeroize(der_sig, sizeof(der_sig)); return ret; } diff --git a/src/tpm.c b/src/tpm.c index a5777337a2..c7149e5e8b 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -44,17 +44,17 @@ WOLFTPM2_KEY wolftpm_srk; #endif #if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE) -int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, +int NOINLINEFUNCTION wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len) { uint32_t i; - volatile uint8_t diff = 0; + volatile uint32_t diff = 0U; for (i = 0; i < len; i++) { - diff |= a[i] ^ b[i]; + diff |= (uint32_t)(a[i] ^ b[i]); } - return diff; + return (int)diff; } void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz, diff --git a/src/update_flash.c b/src/update_flash.c index 6768c65411..1fdbb6d804 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -567,7 +567,7 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume) # define DELTA_BLOCK_SIZE 1024 #endif -static inline uint32_t im2n(uint32_t val) +static inline uint32_t wb_delta_im2n(uint32_t val) { #ifdef BIG_ENDIAN_ORDER return (((val & 0x000000FFU) << 24) | @@ -631,9 +631,9 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, &delta_base_hash, &delta_base_hash_sz) < 0) { return -1; } - delta_img_size = im2n(*img_size); + delta_img_size = wb_delta_im2n(*img_size); if (inverse) - delta_img_offset = im2n(*img_offset); + delta_img_offset = wb_delta_im2n(*img_offset); cur_v = wolfBoot_current_firmware_version(); upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); diff --git a/tools/keytools/otp/Makefile b/tools/keytools/otp/Makefile index 6f9d8f483c..7ad1b8240f 100644 --- a/tools/keytools/otp/Makefile +++ b/tools/keytools/otp/Makefile @@ -19,7 +19,6 @@ CFLAGS+=-I. -I../../../ -I../../../include -I$(WOLFBOOT_LIB_WOLFSSL) \ CFLAGS+=-I./wcs CFLAGS+=-DFLASH_OTP_KEYSTORE -D__FLASH_OTP_PRIMER -DWOLFSSL_USER_SETTINGS -DWOLFCRYPT_SECURE_MODE PRI_KS_OBJS+=startup.o otp-keystore-primer.o ../../../src/keystore.o -PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.o ifeq ($(HASH),SHA256) PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o diff --git a/tools/keytools/otp/target.ld b/tools/keytools/otp/target.ld index 3423493604..61ccd8bf8d 100644 --- a/tools/keytools/otp/target.ld +++ b/tools/keytools/otp/target.ld @@ -15,6 +15,7 @@ SECTIONS *(.fini) *(.text*) *(.rodata*) + KEEP(*(.keystore*)) . = ALIGN(4); _end_text = .; } > FLASH diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index ed69720f74..d40ef743bd 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -573,6 +573,38 @@ START_TEST (test_empty_panic) } END_TEST +START_TEST (test_part_sanity_check_panics_on_sha_mismatch) +{ + struct wolfBoot_image img; + + memset(&img, 0, sizeof(img)); + img.hdr_ok = 1; + img.sha_ok = 0; + img.signature_ok = 1; + wolfBoot_panicked = 0; + + PART_SANITY_CHECK(&img); + ck_assert_int_eq(wolfBoot_panicked, 1); + wolfBoot_panicked = 0; +} +END_TEST + +START_TEST (test_part_sanity_check_panics_on_signature_mismatch) +{ + struct wolfBoot_image img; + + memset(&img, 0, sizeof(img)); + img.hdr_ok = 1; + img.sha_ok = 1; + img.signature_ok = 0; + wolfBoot_panicked = 0; + + PART_SANITY_CHECK(&img); + ck_assert_int_eq(wolfBoot_panicked, 1); + wolfBoot_panicked = 0; +} +END_TEST + #ifdef EXT_ENCRYPTED START_TEST (test_fallback_image_verification_rejects_corruption) { @@ -1297,6 +1329,8 @@ Suite *wolfboot_suite(void) return s; #else tcase_add_test(empty_panic, test_empty_panic); + tcase_add_test(empty_panic, test_part_sanity_check_panics_on_sha_mismatch); + tcase_add_test(empty_panic, test_part_sanity_check_panics_on_signature_mismatch); tcase_add_test(sunnyday_noupdate, test_sunnyday_noupdate); tcase_add_test(forward_update_samesize, test_forward_update_samesize); tcase_add_test(forward_update_tolarger, test_forward_update_tolarger); From 857cd36f675d33b490143666f8afd8990d77e35f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 18:19:44 +0200 Subject: [PATCH 38/41] Addressed another round of reviews (copilot) --- hal/library.c | 2 +- tools/unit-tests/unit-delta.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/hal/library.c b/hal/library.c index dddca8527c..6fb438e3da 100644 --- a/hal/library.c +++ b/hal/library.c @@ -83,7 +83,7 @@ void hal_prepare_boot(void) return; } -static void wolfBoot_panic(void) +static void library_panic(void) { wolfBoot_printf("wolfBoot: PANIC!\n"); exit('P'); diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index d512687a99..47ede1bfbb 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -493,20 +493,36 @@ START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) char *saved_copy = saved ? strdup(saved) : NULL; pid_t pid; int status = 0; + int setenv_ok; + int fork_ok = 0; + int wait_ok = 0; + int exited_ok = 0; + int exit_code = -1; + + setenv_ok = (setenv("WOLFBOOT_SECTOR_SIZE", "0x20000", 1) == 0); + if (!setenv_ok) { + goto restore_env; + } - ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", "0x20000", 1), 0); pid = fork(); - ck_assert_int_ne(pid, -1); + if (pid != -1) { + fork_ok = 1; + } if (pid == 0) { (void)wb_diff_get_sector_size(); _exit(0); } - ck_assert_int_eq(waitpid(pid, &status, 0), pid); - ck_assert(WIFEXITED(status)); - ck_assert_int_eq(WEXITSTATUS(status), 6); + if (fork_ok && (waitpid(pid, &status, 0) == pid)) { + wait_ok = 1; + exited_ok = WIFEXITED(status); + if (exited_ok) { + exit_code = WEXITSTATUS(status); + } + } +restore_env: if (saved_copy != NULL) { ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", saved_copy, 1), 0); free(saved_copy); @@ -514,6 +530,12 @@ START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) else { ck_assert_int_eq(unsetenv("WOLFBOOT_SECTOR_SIZE"), 0); } + + ck_assert(setenv_ok); + ck_assert(fork_ok); + ck_assert(wait_ok); + ck_assert(exited_ok); + ck_assert_int_eq(exit_code, 6); #else ck_assert_int_eq(1, 1); #endif From e6c197675447c4ecac19de87c79769bcd56f8fcf Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 18:54:31 +0200 Subject: [PATCH 39/41] Fixed wolfBoot_panic() visibility in lib + Fenrir comments --- hal/library.c | 6 ++- src/image.c | 6 +-- src/tpm.c | 2 +- tools/unit-tests/unit-delta.c | 26 +++++++++++++ tools/unit-tests/unit-update-flash.c | 58 ++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 5 deletions(-) diff --git a/hal/library.c b/hal/library.c index 6fb438e3da..a0d3a3b92e 100644 --- a/hal/library.c +++ b/hal/library.c @@ -83,7 +83,7 @@ void hal_prepare_boot(void) return; } -static void library_panic(void) +void WEAKFUNCTION wolfBoot_panic(void) { wolfBoot_printf("wolfBoot: PANIC!\n"); exit('P'); @@ -150,6 +150,10 @@ int wolfBoot_start(void) wolfBoot_printf("Firmware Valid\n"); #ifndef WOLFBOOT_SKIP_BOOT_VERIFY + if ((os_image.hdr_ok != 1U) || (os_image.sha_ok != 1U) || + (os_image.signature_ok != 1U)) { + wolfBoot_panic(); + } PART_SANITY_CHECK(&os_image); #endif do_boot((uint32_t*)os_image.fw_base); diff --git a/src/image.c b/src/image.c index 991601aaf1..9f0708c5ae 100644 --- a/src/image.c +++ b/src/image.c @@ -61,14 +61,14 @@ static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4); int NOINLINEFUNCTION image_CT_compare( const uint8_t *expected, const uint8_t *actual, uint32_t len) { - volatile uint8_t diff = 0; + volatile uint32_t diff = 0U; uint32_t i; for (i = 0; i < len; i++) { - diff |= expected[i] ^ actual[i]; + diff |= (uint32_t)(expected[i] ^ actual[i]); } - return diff; + return (diff != 0U) ? 1 : 0; } #if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \ diff --git a/src/tpm.c b/src/tpm.c index c7149e5e8b..2bb799a237 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -54,7 +54,7 @@ int NOINLINEFUNCTION wolfBoot_constant_compare(const uint8_t* a, const uint8_t* diff |= (uint32_t)(a[i] ^ b[i]); } - return (int)diff; + return (diff != 0U) ? 1 : 0; } void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz, diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index 47ede1bfbb..fd016c84d0 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -542,6 +542,31 @@ START_TEST(test_wb_diff_get_sector_size_rejects_values_above_16bit) } END_TEST +START_TEST(test_wb_diff_get_sector_size_accepts_16bit_limit) +{ + const char *saved = getenv("WOLFBOOT_SECTOR_SIZE"); + char *saved_copy = saved ? strdup(saved) : NULL; + int setenv_ok = 0; + int sector_size = 0; + + setenv_ok = (setenv("WOLFBOOT_SECTOR_SIZE", "0xFFFF", 1) == 0); + if (setenv_ok) { + sector_size = wb_diff_get_sector_size(); + } + + if (saved_copy != NULL) { + ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", saved_copy, 1), 0); + free(saved_copy); + } + else { + ck_assert_int_eq(unsetenv("WOLFBOOT_SECTOR_SIZE"), 0); + } + + ck_assert(setenv_ok); + ck_assert_int_eq(sector_size, 0xFFFF); +} +END_TEST + START_TEST(test_wb_patch_and_diff_size_changing_update) { uint8_t src_a[2048]; @@ -601,6 +626,7 @@ Suite *patch_diff_suite(void) tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_completely_different_images); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_all_escape_images); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_multi_sector_images); + tcase_add_test(tc_wolfboot_delta, test_wb_diff_get_sector_size_accepts_16bit_limit); tcase_add_test(tc_wolfboot_delta, test_wb_diff_get_sector_size_rejects_values_above_16bit); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_size_changing_update); tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff_single_byte_difference); diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index d40ef743bd..573274967c 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -85,6 +85,24 @@ static uint16_t host_to_img_u16(uint16_t val) #endif } +static void ext_flash_write_le16(uintptr_t addr, uint16_t val) +{ + uint8_t le[2]; + le[0] = (uint8_t)(val & 0xFFu); + le[1] = (uint8_t)((val >> 8) & 0xFFu); + ext_flash_write(addr, le, sizeof(le)); +} + +static void ext_flash_write_le32(uintptr_t addr, uint32_t val) +{ + uint8_t le[4]; + le[0] = (uint8_t)(val & 0xFFu); + le[1] = (uint8_t)((val >> 8) & 0xFFu); + le[2] = (uint8_t)((val >> 16) & 0xFFu); + le[3] = (uint8_t)((val >> 24) & 0xFFu); + ext_flash_write(addr, le, sizeof(le)); +} + #ifdef DELTA_UPDATES static int mock_wb_patch_init_calls = 0; static uint8_t *mock_wb_patch_init_patch = NULL; @@ -1063,6 +1081,44 @@ START_TEST (test_get_total_size_preserves_uint32_range) } END_TEST +START_TEST (test_diffbase_version_reads_from_little_endian_bytes) +{ + uint32_t magic = WOLFBOOT_MAGIC; + uint16_t img_type = HDR_IMG_TYPE_AUTH | HDR_IMG_TYPE_APP; + uint32_t version = 0x01020304; + uint32_t delta_base = 0x55667788; + uint32_t tag; + + reset_mock_stats(); + prepare_flash(); + + ext_flash_unlock(); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS, + (const uint8_t *)&magic, sizeof(magic)); + ext_flash_write_le32(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 4, TEST_SIZE_SMALL); + + tag = (4u << 16) | HDR_VERSION; + ext_flash_write_le32(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 8, tag); + ext_flash_write_le32(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 12, version); + + tag = (2u << 16) | HDR_IMG_TYPE; + ext_flash_write_le32(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 16, tag); + ext_flash_write_le16(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 20, img_type); + + tag = (4u << 16) | HDR_IMG_DELTA_BASE; + ext_flash_write_le32(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 24, tag); + ext_flash_write_le32(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 28, delta_base); + ext_flash_lock(); + + ck_assert_uint_eq(wolfBoot_get_image_version(PART_UPDATE), version); + ck_assert_uint_eq(wolfBoot_get_diffbase_version(PART_UPDATE), delta_base); + ck_assert_uint_eq(wolfBoot_get_blob_diffbase_version( + (uint8_t *)(uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS), delta_base); + + cleanup_flash(); +} +END_TEST + #ifdef DELTA_UPDATES START_TEST (test_delta_zero_size_valid_header_rejected_without_recovery_heuristic) { @@ -1187,6 +1243,7 @@ START_TEST (test_delta_base_version_match_accepts) ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0); ck_assert_int_eq(ret, 0); ck_assert_int_eq(mock_wb_patch_init_calls, 1); + ck_assert_ptr_eq(mock_wb_patch_init_patch, update.hdr + IMAGE_HEADER_SIZE); ck_assert_uint_eq(mock_wb_patch_init_psz, delta_sz); cleanup_flash(); @@ -1350,6 +1407,7 @@ Suite *wolfboot_suite(void) tcase_add_test(empty_boot_but_update_sha_corrupted_denied, test_empty_boot_but_update_sha_corrupted_denied); tcase_add_test(swap_resume, test_swap_resume_noop); tcase_add_test(diffbase_version, test_diffbase_version_reads); + tcase_add_test(diffbase_version, test_diffbase_version_reads_from_little_endian_bytes); tcase_add_test(get_total_size, test_get_total_size_preserves_uint32_range); tcase_add_test(boot_success, test_boot_success_sets_state); #ifdef DELTA_UPDATES From 6540f17ef21891926b29ac7a276359df606d2ff0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 14 Apr 2026 20:00:41 +0200 Subject: [PATCH 40/41] Fixed more comments from copilot + fenrir --- src/update_flash.c | 11 +++++++++-- src/x86/ahci.c | 14 +++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/update_flash.c b/src/update_flash.c index 1fdbb6d804..04b6e0dd6d 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -632,8 +632,11 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, return -1; } delta_img_size = wb_delta_im2n(*img_size); - if (inverse) + if (inverse) { delta_img_offset = wb_delta_im2n(*img_offset); + } else { + delta_img_offset = IMAGE_HEADER_SIZE; + } cur_v = wolfBoot_current_firmware_version(); upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); @@ -691,7 +694,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, ret = -1; } else { ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + IMAGE_HEADER_SIZE, - update->hdr + IMAGE_HEADER_SIZE, delta_img_size); + update->hdr + delta_img_offset, delta_img_size); } } if (ret < 0) @@ -1488,6 +1491,10 @@ void RAMFUNCTION wolfBoot_start(void) } } } + if ((boot.hdr_ok != 1U) || (boot.sha_ok != 1U) || + (boot.signature_ok != 1U)) { + wolfBoot_panic(); + } PART_SANITY_CHECK(&boot); #else if (bootRet < 0) { diff --git a/src/x86/ahci.c b/src/x86/ahci.c index d5be05fd8a..ba541c497f 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -122,6 +122,14 @@ static int wolfBoot_local_constant_compare(const uint8_t* a, const uint8_t* b, return diff; } +static void ahci_secret_zeroize(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + while (len-- > 0U) { + *p++ = 0U; + } +} + /** * @brief Sets the AHCI Base Address Register (ABAR) for the given device. * @@ -278,7 +286,7 @@ static int sata_get_random_base64(uint8_t *out, int *out_size) ret = 0; cleanup: - TPM2_ForceZero(rand, sizeof(rand)); + ahci_secret_zeroize(rand, sizeof(rand)); return ret; } @@ -322,7 +330,7 @@ static int sata_create_and_seal_unlock_secret(const uint8_t *pubkey_hint, } wolfBoot_printf("Secret Check %d bytes\n", secret_check_sz); - TPM2_ForceZero(secret_check, sizeof(secret_check)); + ahci_secret_zeroize(secret_check, sizeof(secret_check)); } if (ret == 0) @@ -495,7 +503,7 @@ int sata_unlock_disk(int drv, int freeze) error: r = -1; cleanup: - TPM2_ForceZero(secret, sizeof(secret)); + ahci_secret_zeroize(secret, sizeof(secret)); return r; } #endif /* WOLFBOOT_ATA_DISK_LOCK */ From 0809d83958b583b83c4130c4992ea234708a7fe1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 01:03:42 +0200 Subject: [PATCH 41/41] Fix actual overlap in hifive config --- config/examples/hifive1.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/examples/hifive1.config b/config/examples/hifive1.config index 4f58c42978..9c17163edf 100644 --- a/config/examples/hifive1.config +++ b/config/examples/hifive1.config @@ -14,7 +14,7 @@ V?=0 SPMATH?=1 RAM_CODE?=1 DUALBANK_SWAP?=0 -WOLFBOOT_PARTITION_SIZE?=0x80000 +WOLFBOOT_PARTITION_SIZE?=0x40000 WOLFBOOT_SECTOR_SIZE?=0x1000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x20020000 WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x20060000