From 61731cabd9930086c896809850344586777dcf98 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 17:25:15 +0200 Subject: [PATCH 01/10] Zeroize PolicySign key buffer before free F/2278 --- tools/tpm/policy_sign.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/tpm/policy_sign.c b/tools/tpm/policy_sign.c index 80ee12d182..2afe408ab3 100644 --- a/tools/tpm/policy_sign.c +++ b/tools/tpm/policy_sign.c @@ -159,7 +159,10 @@ static int PolicySign(int alg, const char* keyFile, byte* hash, word32 hashSz, rc = BAD_FUNC_ARG; } - XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (buf != NULL) { + wc_ForceZero(buf, bufSz); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } wc_FreeRng(&rng); if (rc != 0) { From 91e0b1678861eeae5612733d6476a70d31a3d021 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 17:26:18 +0200 Subject: [PATCH 02/10] keygen_lms: zero LmsKey after free F/2589 --- tools/keytools/keygen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index 1b99309f79..5a3ed319c7 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -948,6 +948,7 @@ static void keygen_lms(const char *priv_fname, uint32_t id_mask) keystore_add(AUTH_KEY_LMS, lms_pub, KEYSTORE_PUBKEY_SIZE_LMS, priv_fname, id_mask); wc_LmsKey_Free(&key); + wc_ForceZero(&key, sizeof(key)); } #include "../xmss/xmss_common.h" From 1359aac80d65fbae63ce8bdc264ed631cf3c0562 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 17:31:29 +0200 Subject: [PATCH 03/10] Block rollback fallback in non-flash update paths F/2254 --- src/libwolfboot.c | 9 +++++++++ src/update_disk.c | 11 +++++++++++ src/update_flash_hwswap.c | 12 ++++++++++++ src/update_ram.c | 15 +++++++++++++++ tools/unit-tests/unit-update-disk.c | 18 ++++++++++++++++++ tools/unit-tests/unit-update-ram-nofixed.c | 8 ++++---- tools/unit-tests/unit-update-ram.c | 20 ++++++++++---------- 7 files changed, 79 insertions(+), 14 deletions(-) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index f5161446d5..99db6ee979 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1395,6 +1395,15 @@ int wolfBoot_dualboot_candidate(void) (wolfBoot_get_partition_state(candidate, &p_state) == 0) && (p_state == IMG_STATE_TESTING)) { +#ifndef ALLOW_DOWNGRADE + uint32_t candidate_v = (candidate == PART_BOOT) ? boot_v : update_v; + uint32_t fallback_v = (candidate == PART_BOOT) ? update_v : boot_v; + + if (fallback_v < candidate_v) { + wolfBoot_printf("Rollback to lower version not allowed\n"); + return -1; + } +#endif wolfBoot_erase_partition(candidate); candidate ^= 1; /* switch to other partition if available */ } diff --git a/src/update_disk.c b/src/update_disk.c index 244d911582..bb72912264 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -259,6 +259,7 @@ void RAMFUNCTION wolfBoot_start(void) uint32_t *load_address; int failures = 0; uint32_t load_off; + uint32_t max_ver; const uint8_t *hdr_ptr = NULL; #ifdef MMU uint8_t *dts_addr = NULL; @@ -346,6 +347,7 @@ void RAMFUNCTION wolfBoot_start(void) } wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver, pB_ver); + max_ver = (pB_ver > pA_ver) ? (uint32_t)pB_ver : (uint32_t)pA_ver; /* Choose partition with higher version */ selected = (pB_ver > pA_ver) ? 1: 0; @@ -368,6 +370,15 @@ void RAMFUNCTION wolfBoot_start(void) cur_part = BOOT_PART_B; else cur_part = BOOT_PART_A; +#ifndef ALLOW_DOWNGRADE + { + uint32_t cur_ver = selected ? (uint32_t)pB_ver : (uint32_t)pA_ver; + if ((max_ver > 0U) && (cur_ver < max_ver)) { + wolfBoot_printf("Rollback to lower version not allowed\r\n"); + break; + } + } +#endif part_name[2] = 'A' + selected; diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index 65ce639184..b0725404c6 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -45,12 +45,24 @@ void RAMFUNCTION wolfBoot_start(void) int active; struct wolfBoot_image fw_image; uint8_t p_state; +#ifndef ALLOW_DOWNGRADE + uint32_t boot_v = wolfBoot_current_firmware_version(); + uint32_t update_v = wolfBoot_update_firmware_version(); + uint32_t max_v = (boot_v > update_v) ? boot_v : update_v; +#endif active = wolfBoot_dualboot_candidate(); if (active < 0) /* panic if no images available */ boot_panic(); for (;;) { +#ifndef ALLOW_DOWNGRADE + uint32_t active_v = (active == PART_UPDATE) ? update_v : boot_v; + if ((max_v > 0U) && (active_v < max_v)) { + wolfBoot_printf("Rollback to lower version not allowed\n"); + boot_panic(); + } +#endif if ((wolfBoot_open_image(&fw_image, active) < 0) #ifndef WOLFBOOT_SKIP_BOOT_VERIFY || (wolfBoot_verify_integrity(&fw_image) < 0) diff --git a/src/update_ram.c b/src/update_ram.c index 247299064c..902f1ad49d 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -138,6 +138,11 @@ void RAMFUNCTION wolfBoot_start(void) uint8_t *dts_addr = NULL; uint32_t dts_size = 0; #endif +#ifndef ALLOW_DOWNGRADE + uint32_t boot_v = wolfBoot_current_firmware_version(); + uint32_t update_v = wolfBoot_update_firmware_version(); + uint32_t max_v = (boot_v > update_v) ? boot_v : update_v; +#endif memset(&os_image, 0, sizeof(struct wolfBoot_image)); @@ -162,6 +167,16 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_panic(); break; } +#ifndef ALLOW_DOWNGRADE + { + uint32_t active_v = (active == PART_UPDATE) ? update_v : boot_v; + if ((max_v > 0U) && (active_v < max_v)) { + wolfBoot_printf("Rollback to lower version not allowed\n"); + wolfBoot_panic(); + break; + } + } +#endif #if defined(WOLFBOOT_DUALBOOT) && defined(WOLFBOOT_FIXED_PARTITIONS) wolfBoot_printf("Trying %s partition at %p\n", diff --git a/tools/unit-tests/unit-update-disk.c b/tools/unit-tests/unit-update-disk.c index 2e7831ded3..80d00c438e 100644 --- a/tools/unit-tests/unit-update-disk.c +++ b/tools/unit-tests/unit-update-disk.c @@ -33,6 +33,7 @@ static int mock_disk_init_ret; static int mock_disk_close_called; static int mock_do_boot_called; static const uint32_t *mock_boot_address; +static int mock_fail_payload_part; ChaCha chacha; @@ -72,6 +73,7 @@ static void reset_mocks(void) mock_disk_close_called = 0; mock_do_boot_called = 0; mock_boot_address = NULL; + mock_fail_payload_part = -1; wolfBoot_panicked = 0; } @@ -140,6 +142,8 @@ int disk_part_read(int drv, int part, uint64_t off, uint64_t sz, uint8_t *buf) (void)drv; image = (part == BOOT_PART_B) ? part_b_image : part_a_image; + if ((mock_fail_payload_part == part) && (off >= IMAGE_HEADER_SIZE)) + return -1; if ((off > max) || (sz > (max - off))) return -1; memcpy(buf, image + off, (size_t)sz); @@ -288,6 +292,19 @@ START_TEST(test_get_decrypted_blob_version_rejects_truncated_version_tlv) } END_TEST +START_TEST(test_update_disk_rejects_rollback_after_higher_image_failure) +{ + reset_mocks(); + build_image(part_a_image, 7, 0xA1); + build_image(part_b_image, 5, 0xB2); + mock_fail_payload_part = BOOT_PART_A; + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_panicked, 1); +} +END_TEST + Suite *wolfboot_suite(void) { Suite *s = suite_create("wolfBoot"); @@ -297,6 +314,7 @@ Suite *wolfboot_suite(void) tcase_add_test(tc, test_update_disk_zeroizes_key_material_before_boot); tcase_add_test(tc, test_update_disk_prefers_primary_partition_when_versions_equal); tcase_add_test(tc, test_get_decrypted_blob_version_rejects_truncated_version_tlv); + tcase_add_test(tc, test_update_disk_rejects_rollback_after_higher_image_failure); suite_add_tcase(s, tc); return s; diff --git a/tools/unit-tests/unit-update-ram-nofixed.c b/tools/unit-tests/unit-update-ram-nofixed.c index 39d047f9ac..1b936a983b 100644 --- a/tools/unit-tests/unit-update-ram-nofixed.c +++ b/tools/unit-tests/unit-update-ram-nofixed.c @@ -191,7 +191,7 @@ static int add_payload(uint8_t part, uint32_t version, uint32_t size) return 0; } -START_TEST(test_invalid_update_falls_back_to_boot_without_reselect_loop) +START_TEST(test_invalid_update_rollback_to_older_boot_is_denied) { uint8_t bad_digest[SHA256_DIGEST_SIZE]; @@ -208,8 +208,8 @@ START_TEST(test_invalid_update_falls_back_to_boot_without_reselect_loop) wolfBoot_start(); - ck_assert_int_eq(wolfBoot_staged_ok, 1); - ck_assert_ptr_eq(wolfBoot_stage_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS); + ck_assert_int_eq(wolfBoot_staged_ok, 0); + ck_assert_int_eq(wolfBoot_panicked, 1); cleanup_flash(); } END_TEST @@ -219,7 +219,7 @@ 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_add_test(tc, test_invalid_update_rollback_to_older_boot_is_denied); tcase_set_timeout(tc, 5); suite_add_tcase(s, tc); diff --git a/tools/unit-tests/unit-update-ram.c b/tools/unit-tests/unit-update-ram.c index fecac50540..2837629fa8 100644 --- a/tools/unit-tests/unit-update-ram.c +++ b/tools/unit-tests/unit-update-ram.c @@ -378,8 +378,8 @@ START_TEST (test_invalid_update_type) { ext_flash_lock(); wolfBoot_update_trigger(); wolfBoot_start(); - ck_assert(wolfBoot_staged_ok); - ck_assert(get_version_ramloaded() == 1); + ck_assert(!wolfBoot_staged_ok); + ck_assert_int_eq(wolfBoot_panicked, 1); cleanup_flash(); } @@ -396,8 +396,8 @@ START_TEST (test_update_toolarge) { wolfBoot_update_trigger(); wolfBoot_start(); - ck_assert(wolfBoot_staged_ok); - ck_assert(get_version_ramloaded() == 1); + ck_assert(!wolfBoot_staged_ok); + ck_assert_int_eq(wolfBoot_panicked, 1); cleanup_flash(); } @@ -414,12 +414,12 @@ START_TEST (test_invalid_sha) { ext_flash_lock(); wolfBoot_update_trigger(); wolfBoot_start(); - ck_assert(wolfBoot_staged_ok); - ck_assert(get_version_ramloaded() == 1); + ck_assert(!wolfBoot_staged_ok); + ck_assert_int_eq(wolfBoot_panicked, 1); cleanup_flash(); } -START_TEST (test_emergency_rollback) { +START_TEST (test_emergency_rollback_to_older_version_denied) { uint8_t testing_flags[5] = { IMG_STATE_TESTING, 'B', 'O', 'O', 'T' }; reset_mock_stats(); prepare_flash(); @@ -432,8 +432,8 @@ START_TEST (test_emergency_rollback) { ext_flash_lock(); wolfBoot_start(); - ck_assert(wolfBoot_staged_ok); - ck_assert(get_version_ramloaded() == 1); + ck_assert(!wolfBoot_staged_ok); + ck_assert_int_eq(wolfBoot_panicked, 1); cleanup_flash(); } @@ -532,7 +532,7 @@ Suite *wolfboot_suite(void) tcase_add_test(invalid_update_type, test_invalid_update_type); tcase_add_test(update_toolarge, test_update_toolarge); tcase_add_test(invalid_sha, test_invalid_sha); - tcase_add_test(emergency_rollback, test_emergency_rollback); + tcase_add_test(emergency_rollback, test_emergency_rollback_to_older_version_denied); tcase_add_test(emergency_rollback_failure_due_to_bad_update, test_emergency_rollback_failure_due_to_bad_update); tcase_add_test(empty_boot_partition_update, test_empty_boot_partition_update); tcase_add_test(empty_boot_but_update_sha_corrupted_denied, test_empty_boot_but_update_sha_corrupted_denied); From d4f062a773fd011951fc2e567d1c95f5fa27382a Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 17:33:37 +0200 Subject: [PATCH 04/10] tools/tpm: write policy output in binary mode F/1896 --- tools/tpm/policy_create.c | 2 +- tools/tpm/policy_sign.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/tpm/policy_create.c b/tools/tpm/policy_create.c index 15b8555547..b4115d9041 100644 --- a/tools/tpm/policy_create.c +++ b/tools/tpm/policy_create.c @@ -54,7 +54,7 @@ int writeBin(const char* filename, const uint8_t*buf, word32 bufSz) XFILE fp = NULL; size_t fileSz = 0; - fp = XFOPEN(filename, "wt"); + fp = XFOPEN(filename, "wb"); if (fp != XBADFILE) { fileSz = XFWRITE(buf, 1, bufSz, fp); /* sanity check */ diff --git a/tools/tpm/policy_sign.c b/tools/tpm/policy_sign.c index 2afe408ab3..e0ee352ef4 100644 --- a/tools/tpm/policy_sign.c +++ b/tools/tpm/policy_sign.c @@ -224,7 +224,7 @@ static int writeBin(const char* filename, const byte *buf, word32 bufSz) FILE *fp = NULL; size_t fileSz = 0; - fp = fopen(filename, "wt"); + fp = fopen(filename, "wb"); if (fp != NULL) { fileSz = fwrite(buf, 1, bufSz, fp); /* sanity check */ From 3118f7ad4eba2e1aec7a795ed9b9836667536fee Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 18:26:04 +0200 Subject: [PATCH 05/10] Fixed regression in sim test, added sim test for denied rollback --- .github/workflows/test-hooks-simulator.yml | 5 ++ .github/workflows/test-sunnyday-simulator.yml | 4 ++ src/update_flash_hwswap.c | 1 + tools/scripts/sim-dualbank-rollback-denied.sh | 65 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100755 tools/scripts/sim-dualbank-rollback-denied.sh diff --git a/.github/workflows/test-hooks-simulator.yml b/.github/workflows/test-hooks-simulator.yml index 23f385ac70..2db52abe72 100644 --- a/.github/workflows/test-hooks-simulator.yml +++ b/.github/workflows/test-hooks-simulator.yml @@ -88,6 +88,11 @@ jobs: WOLFBOOT_HOOK_BOOT=1 \ WOLFBOOT_HOOK_PANIC=1 + - name: Run dualbank rollback denial simulation + if: matrix.mechanism == 'dualbank' + run: | + tools/scripts/sim-dualbank-rollback-denied.sh + - name: Clear hook log run: | rm -f /tmp/wolfboot_hooks.log diff --git a/.github/workflows/test-sunnyday-simulator.yml b/.github/workflows/test-sunnyday-simulator.yml index f8b3dd4b73..d17eda864e 100644 --- a/.github/workflows/test-sunnyday-simulator.yml +++ b/.github/workflows/test-sunnyday-simulator.yml @@ -54,6 +54,10 @@ jobs: cp config/examples/sim-dualbank.config .config make test-sim-internal-flash-with-update + - name: Run dualbank rollback denial simulation + run: | + tools/scripts/sim-dualbank-rollback-denied.sh + - name: Run dualbank swap simulation run: | tools/scripts/sim-dualbank-swap-update.sh diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index b0725404c6..8548fbfd29 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -28,6 +28,7 @@ #include "hooks.h" #include "spi_flash.h" #include "wolfboot/wolfboot.h" +#include "printf.h" #ifdef SECURE_PKCS11 int WP11_Library_Init(void); #endif diff --git a/tools/scripts/sim-dualbank-rollback-denied.sh b/tools/scripts/sim-dualbank-rollback-denied.sh new file mode 100755 index 0000000000..5cf18001e5 --- /dev/null +++ b/tools/scripts/sim-dualbank-rollback-denied.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -euo pipefail + +if [ ! -f ".config" ]; then + echo "Missing .config. Run make config first." >&2 + exit 1 +fi + +if ! grep -Eq '^(DUALBANK_SWAP(\?|)=1)' .config; then + echo "DUALBANK_SWAP=1 is required for this simulation." >&2 + exit 1 +fi + +if [ ! -x "./wolfboot.elf" ]; then + echo "wolfboot.elf not found. Build the simulator first." >&2 + exit 1 +fi + +if [ ! -f "./internal_flash.dd" ]; then + echo "internal_flash.dd not found. Build test-sim-internal-flash-with-update first." >&2 + exit 1 +fi + +backup_image="$(mktemp ./internal_flash.rollback.XXXXXX)" +cp ./internal_flash.dd "$backup_image" +trap 'cp "$backup_image" ./internal_flash.dd; rm -f "$backup_image" sim_registers.dd' EXIT + +rm -f sim_registers.dd + +update_addr_hex="$(grep '^WOLFBOOT_PARTITION_UPDATE_ADDRESS=' .config | cut -d= -f2)" +if [ -z "${update_addr_hex}" ]; then + echo "WOLFBOOT_PARTITION_UPDATE_ADDRESS is not set in .config." >&2 + exit 1 +fi + +update_addr=$((update_addr_hex)) + +# Corrupt UPDATE payload bytes so version metadata remains intact but +# image verification fails and boot logic attempts fallback. +printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \ + dd of=./internal_flash.dd bs=1 seek="$((update_addr + 0x120))" conv=notrunc status=none + +set +e +rollback_output="$(timeout 3s ./wolfboot.elf get_version 2>&1)" +rollback_rc=$? +set -e + +if [ "$rollback_rc" -eq 0 ]; then + echo "Expected rollback denial, but boot continued normally." >&2 + exit 1 +fi + +if [ "$rollback_rc" -ne 124 ] && [ "$rollback_rc" -ne 80 ]; then + echo "Unexpected exit code while checking rollback denial: $rollback_rc" >&2 + echo "$rollback_output" >&2 + exit 1 +fi + +if ! printf '%s\n' "$rollback_output" | grep -q "Rollback to lower version not allowed"; then + echo "Rollback denial message not found in output." >&2 + echo "$rollback_output" >&2 + exit 1 +fi + +echo "Dualbank rollback-to-older-version denial verified." From 26552b5aed30a9029e46b7fb0ff2feabf15b17b5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 18:32:25 +0200 Subject: [PATCH 06/10] Addressed copilot's comments --- src/libwolfboot.c | 1 + src/update_flash_hwswap.c | 1 + tools/unit-tests/unit-update-ram-nofixed.c | 1 + tools/unit-tests/unit-update-ram.c | 1 + 4 files changed, 4 insertions(+) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 99db6ee979..6e877e3b7b 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1401,6 +1401,7 @@ int wolfBoot_dualboot_candidate(void) if (fallback_v < candidate_v) { wolfBoot_printf("Rollback to lower version not allowed\n"); + wolfBoot_erase_partition(candidate); return -1; } #endif diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index 8548fbfd29..d2c4900bc6 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -62,6 +62,7 @@ void RAMFUNCTION wolfBoot_start(void) if ((max_v > 0U) && (active_v < max_v)) { wolfBoot_printf("Rollback to lower version not allowed\n"); boot_panic(); + continue; } #endif if ((wolfBoot_open_image(&fw_image, active) < 0) diff --git a/tools/unit-tests/unit-update-ram-nofixed.c b/tools/unit-tests/unit-update-ram-nofixed.c index 1b936a983b..52efec6d6b 100644 --- a/tools/unit-tests/unit-update-ram-nofixed.c +++ b/tools/unit-tests/unit-update-ram-nofixed.c @@ -210,6 +210,7 @@ START_TEST(test_invalid_update_rollback_to_older_boot_is_denied) ck_assert_int_eq(wolfBoot_staged_ok, 0); ck_assert_int_eq(wolfBoot_panicked, 1); + ck_assert_ptr_eq((void *)wolfBoot_stage_address, (void *)0xFFFFFFFF); cleanup_flash(); } END_TEST diff --git a/tools/unit-tests/unit-update-ram.c b/tools/unit-tests/unit-update-ram.c index 2837629fa8..4c94bbc760 100644 --- a/tools/unit-tests/unit-update-ram.c +++ b/tools/unit-tests/unit-update-ram.c @@ -380,6 +380,7 @@ START_TEST (test_invalid_update_type) { wolfBoot_start(); ck_assert(!wolfBoot_staged_ok); ck_assert_int_eq(wolfBoot_panicked, 1); + ck_assert_int_eq(get_version_ramloaded(), 2); cleanup_flash(); } From 0a4f8281a2d8cb878c9e659f3398e6afe97ae4b6 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 18:57:13 +0200 Subject: [PATCH 07/10] Addressed Fenrir's comments --- src/update_disk.c | 4 +++- src/update_flash_hwswap.c | 2 +- tools/unit-tests/unit-update-disk.c | 3 ++- tools/unit-tests/unit-update-ram.c | 26 ++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/update_disk.c b/src/update_disk.c index bb72912264..67249f0585 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -375,7 +375,8 @@ void RAMFUNCTION wolfBoot_start(void) uint32_t cur_ver = selected ? (uint32_t)pB_ver : (uint32_t)pA_ver; if ((max_ver > 0U) && (cur_ver < max_ver)) { wolfBoot_printf("Rollback to lower version not allowed\r\n"); - break; + wolfBoot_panic(); + return; } } #endif @@ -509,6 +510,7 @@ void RAMFUNCTION wolfBoot_start(void) #endif wolfBoot_printf("Unable to find a valid partition!\r\n"); wolfBoot_panic(); + return; } disk_close(BOOT_DISK); diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index d2c4900bc6..e7673a815d 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -62,7 +62,7 @@ void RAMFUNCTION wolfBoot_start(void) if ((max_v > 0U) && (active_v < max_v)) { wolfBoot_printf("Rollback to lower version not allowed\n"); boot_panic(); - continue; + return; } #endif if ((wolfBoot_open_image(&fw_image, active) < 0) diff --git a/tools/unit-tests/unit-update-disk.c b/tools/unit-tests/unit-update-disk.c index 80d00c438e..a80acafb80 100644 --- a/tools/unit-tests/unit-update-disk.c +++ b/tools/unit-tests/unit-update-disk.c @@ -301,7 +301,8 @@ START_TEST(test_update_disk_rejects_rollback_after_higher_image_failure) wolfBoot_start(); - ck_assert_int_eq(wolfBoot_panicked, 1); + ck_assert_int_gt(wolfBoot_panicked, 0); + ck_assert_int_eq(mock_do_boot_called, 0); } END_TEST diff --git a/tools/unit-tests/unit-update-ram.c b/tools/unit-tests/unit-update-ram.c index 4c94bbc760..61920f290d 100644 --- a/tools/unit-tests/unit-update-ram.c +++ b/tools/unit-tests/unit-update-ram.c @@ -438,6 +438,26 @@ START_TEST (test_emergency_rollback_to_older_version_denied) { cleanup_flash(); } +START_TEST (test_dualboot_candidate_rejects_testing_rollback_to_lower_version) { + uint8_t testing_flags[5] = { IMG_STATE_TESTING, 'B', 'O', 'O', 'T' }; + int candidate; + + reset_mock_stats(); + prepare_flash(); + add_payload(PART_BOOT, 2, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, 1, TEST_SIZE_SMALL); + + ext_flash_unlock(); + ext_flash_write(WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - 5, + testing_flags, 5); + ext_flash_lock(); + + candidate = wolfBoot_dualboot_candidate(); + ck_assert_int_eq(candidate, -1); + ck_assert_uint_eq(wolfBoot_current_firmware_version(), 0U); + cleanup_flash(); +} + START_TEST (test_emergency_rollback_failure_due_to_bad_update) { uint8_t testing_flags[5] = { IMG_STATE_TESTING, 'B', 'O', 'O', 'T' }; uint8_t wrong_update_magic[4] = { 'G', 'O', 'L', 'F' }; @@ -514,6 +534,8 @@ Suite *wolfboot_suite(void) TCase *update_toolarge = tcase_create("Update too large"); TCase *invalid_sha = tcase_create("Invalid SHA digest"); TCase *emergency_rollback = tcase_create("Emergency rollback"); + TCase *dualboot_candidate_rollback_denied = + tcase_create("Dualboot candidate rollback denied"); TCase *emergency_rollback_failure_due_to_bad_update = tcase_create("Emergency rollback failure due to bad update"); TCase *empty_boot_partition_update = tcase_create("Empty boot partition update"); TCase *empty_boot_but_update_sha_corrupted_denied = tcase_create("Empty boot partition but update SHA corrupted"); @@ -534,6 +556,8 @@ Suite *wolfboot_suite(void) tcase_add_test(update_toolarge, test_update_toolarge); tcase_add_test(invalid_sha, test_invalid_sha); tcase_add_test(emergency_rollback, test_emergency_rollback_to_older_version_denied); + tcase_add_test(dualboot_candidate_rollback_denied, + test_dualboot_candidate_rejects_testing_rollback_to_lower_version); tcase_add_test(emergency_rollback_failure_due_to_bad_update, test_emergency_rollback_failure_due_to_bad_update); tcase_add_test(empty_boot_partition_update, test_empty_boot_partition_update); tcase_add_test(empty_boot_but_update_sha_corrupted_denied, test_empty_boot_but_update_sha_corrupted_denied); @@ -554,6 +578,7 @@ Suite *wolfboot_suite(void) suite_add_tcase(s, update_toolarge); suite_add_tcase(s, invalid_sha); suite_add_tcase(s, emergency_rollback); + suite_add_tcase(s, dualboot_candidate_rollback_denied); suite_add_tcase(s, emergency_rollback_failure_due_to_bad_update); suite_add_tcase(s, empty_boot_partition_update); suite_add_tcase(s, empty_boot_but_update_sha_corrupted_denied); @@ -574,6 +599,7 @@ Suite *wolfboot_suite(void) tcase_set_timeout(update_toolarge, 5); tcase_set_timeout(invalid_sha, 5); tcase_set_timeout(emergency_rollback, 5); + tcase_set_timeout(dualboot_candidate_rollback_denied, 5); tcase_set_timeout(emergency_rollback_failure_due_to_bad_update, 5); tcase_set_timeout(empty_boot_partition_update, 5); tcase_set_timeout(empty_boot_but_update_sha_corrupted_denied, 5); From 90670fcdba9260fabfbe9c7364510bf99aaceb65 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 19:02:06 +0200 Subject: [PATCH 08/10] Addressed the 3 remaining open Copilot comments. --- src/update_flash_hwswap.c | 12 ++++++++++-- src/update_ram.c | 12 ++++++++++-- tools/unit-tests/unit-update-ram-nofixed.c | 3 ++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/update_flash_hwswap.c b/src/update_flash_hwswap.c index e7673a815d..47d66e7d0f 100644 --- a/src/update_flash_hwswap.c +++ b/src/update_flash_hwswap.c @@ -47,9 +47,17 @@ void RAMFUNCTION wolfBoot_start(void) struct wolfBoot_image fw_image; uint8_t p_state; #ifndef ALLOW_DOWNGRADE - uint32_t boot_v = wolfBoot_current_firmware_version(); - uint32_t update_v = wolfBoot_update_firmware_version(); + int boot_v_raw = (int)wolfBoot_current_firmware_version(); + int update_v_raw = (int)wolfBoot_update_firmware_version(); + uint32_t boot_v = 0U; + uint32_t update_v = 0U; uint32_t max_v = (boot_v > update_v) ? boot_v : update_v; + + if (boot_v_raw >= 0) + boot_v = (uint32_t)boot_v_raw; + if (update_v_raw >= 0) + update_v = (uint32_t)update_v_raw; + max_v = (boot_v > update_v) ? boot_v : update_v; #endif active = wolfBoot_dualboot_candidate(); diff --git a/src/update_ram.c b/src/update_ram.c index 902f1ad49d..8f27a6363b 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -139,9 +139,17 @@ void RAMFUNCTION wolfBoot_start(void) uint32_t dts_size = 0; #endif #ifndef ALLOW_DOWNGRADE - uint32_t boot_v = wolfBoot_current_firmware_version(); - uint32_t update_v = wolfBoot_update_firmware_version(); + int boot_v_raw = (int)wolfBoot_current_firmware_version(); + int update_v_raw = (int)wolfBoot_update_firmware_version(); + uint32_t boot_v = 0U; + uint32_t update_v = 0U; uint32_t max_v = (boot_v > update_v) ? boot_v : update_v; + + if (boot_v_raw >= 0) + boot_v = (uint32_t)boot_v_raw; + if (update_v_raw >= 0) + update_v = (uint32_t)update_v_raw; + max_v = (boot_v > update_v) ? boot_v : update_v; #endif memset(&os_image, 0, sizeof(struct wolfBoot_image)); diff --git a/tools/unit-tests/unit-update-ram-nofixed.c b/tools/unit-tests/unit-update-ram-nofixed.c index 52efec6d6b..8495237a6c 100644 --- a/tools/unit-tests/unit-update-ram-nofixed.c +++ b/tools/unit-tests/unit-update-ram-nofixed.c @@ -27,6 +27,7 @@ static __thread unsigned char #define WOLFBOOT_LOAD_ADDRESS (((uintptr_t)wolfboot_ram) + IMAGE_HEADER_SIZE) #define TEST_SIZE_SMALL 5300 #define DIGEST_TLV_OFF_IN_HDR 28 +#define STAGE_ADDR_SENTINEL ((uintptr_t)0xFFFFFFFFu) #include "user_settings.h" #include "wolfboot/wolfboot.h" @@ -210,7 +211,7 @@ START_TEST(test_invalid_update_rollback_to_older_boot_is_denied) ck_assert_int_eq(wolfBoot_staged_ok, 0); ck_assert_int_eq(wolfBoot_panicked, 1); - ck_assert_ptr_eq((void *)wolfBoot_stage_address, (void *)0xFFFFFFFF); + ck_assert_uint_eq((uintptr_t)wolfBoot_stage_address, STAGE_ADDR_SENTINEL); cleanup_flash(); } END_TEST From 8d9e912fb8c7f5640af46304498f7e1efaa0c334 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 20:37:07 +0200 Subject: [PATCH 09/10] Addressed another round of copilot comments --- src/libwolfboot.c | 3 +-- src/update_disk.c | 14 ++++++++++---- src/update_ram.c | 2 +- tools/unit-tests/unit-update-ram-nofixed.c | 5 +++-- tools/unit-tests/unit-update-ram.c | 9 +++++---- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 6e877e3b7b..a5610e493b 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1401,8 +1401,7 @@ int wolfBoot_dualboot_candidate(void) if (fallback_v < candidate_v) { wolfBoot_printf("Rollback to lower version not allowed\n"); - wolfBoot_erase_partition(candidate); - return -1; + return candidate; } #endif wolfBoot_erase_partition(candidate); diff --git a/src/update_disk.c b/src/update_disk.c index 67249f0585..cf11258733 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -253,6 +253,7 @@ void RAMFUNCTION wolfBoot_start(void) #endif struct wolfBoot_image os_image; int pA_ver = 0, pB_ver = 0; + uint32_t pA_ver_u = 0U, pB_ver_u = 0U; uint32_t cur_part = 0; int ret = -1; int selected; @@ -346,11 +347,16 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_panic(); } - wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver, pB_ver); - max_ver = (pB_ver > pA_ver) ? (uint32_t)pB_ver : (uint32_t)pA_ver; + if (pA_ver > 0) + pA_ver_u = (uint32_t)pA_ver; + if (pB_ver > 0) + pB_ver_u = (uint32_t)pB_ver; + + wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver_u, pB_ver_u); + max_ver = (pB_ver_u > pA_ver_u) ? pB_ver_u : pA_ver_u; /* Choose partition with higher version */ - selected = (pB_ver > pA_ver) ? 1: 0; + selected = (pB_ver_u > pA_ver_u) ? 1 : 0; #ifdef WOLFBOOT_FSP stage2_params = stage2_get_parameters(); @@ -372,7 +378,7 @@ void RAMFUNCTION wolfBoot_start(void) cur_part = BOOT_PART_A; #ifndef ALLOW_DOWNGRADE { - uint32_t cur_ver = selected ? (uint32_t)pB_ver : (uint32_t)pA_ver; + uint32_t cur_ver = selected ? pB_ver_u : pA_ver_u; if ((max_ver > 0U) && (cur_ver < max_ver)) { wolfBoot_printf("Rollback to lower version not allowed\r\n"); wolfBoot_panic(); diff --git a/src/update_ram.c b/src/update_ram.c index 8f27a6363b..9d9655abb1 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -143,7 +143,7 @@ void RAMFUNCTION wolfBoot_start(void) int update_v_raw = (int)wolfBoot_update_firmware_version(); uint32_t boot_v = 0U; uint32_t update_v = 0U; - uint32_t max_v = (boot_v > update_v) ? boot_v : update_v; + uint32_t max_v = 0U; if (boot_v_raw >= 0) boot_v = (uint32_t)boot_v_raw; diff --git a/tools/unit-tests/unit-update-ram-nofixed.c b/tools/unit-tests/unit-update-ram-nofixed.c index 8495237a6c..343739742f 100644 --- a/tools/unit-tests/unit-update-ram-nofixed.c +++ b/tools/unit-tests/unit-update-ram-nofixed.c @@ -27,7 +27,7 @@ static __thread unsigned char #define WOLFBOOT_LOAD_ADDRESS (((uintptr_t)wolfboot_ram) + IMAGE_HEADER_SIZE) #define TEST_SIZE_SMALL 5300 #define DIGEST_TLV_OFF_IN_HDR 28 -#define STAGE_ADDR_SENTINEL ((uintptr_t)0xFFFFFFFFu) +#define STAGE_ADDR_SENTINEL UINTPTR_MAX #include "user_settings.h" #include "wolfboot/wolfboot.h" @@ -53,7 +53,8 @@ int wolfBoot_dualboot_candidate_addr(void** addr) #include int wolfBoot_staged_ok = 0; -const uint32_t *wolfBoot_stage_address = (uint32_t *)0xFFFFFFFF; +const uint32_t *wolfBoot_stage_address = + (const uint32_t *)(uintptr_t)STAGE_ADDR_SENTINEL; void* hal_get_primary_address(void) { diff --git a/tools/unit-tests/unit-update-ram.c b/tools/unit-tests/unit-update-ram.c index 61920f290d..3f262d5665 100644 --- a/tools/unit-tests/unit-update-ram.c +++ b/tools/unit-tests/unit-update-ram.c @@ -433,8 +433,9 @@ START_TEST (test_emergency_rollback_to_older_version_denied) { ext_flash_lock(); wolfBoot_start(); - ck_assert(!wolfBoot_staged_ok); - ck_assert_int_eq(wolfBoot_panicked, 1); + ck_assert(wolfBoot_staged_ok); + ck_assert_int_eq(get_version_ramloaded(), 2); + ck_assert_int_eq(wolfBoot_panicked, 0); cleanup_flash(); } @@ -453,8 +454,8 @@ START_TEST (test_dualboot_candidate_rejects_testing_rollback_to_lower_version) { ext_flash_lock(); candidate = wolfBoot_dualboot_candidate(); - ck_assert_int_eq(candidate, -1); - ck_assert_uint_eq(wolfBoot_current_firmware_version(), 0U); + ck_assert_int_eq(candidate, PART_BOOT); + ck_assert_uint_eq(wolfBoot_current_firmware_version(), 2U); cleanup_flash(); } From 73c29622e11752f32c264698936cfd79510d7dbf Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 15 Apr 2026 23:22:00 +0200 Subject: [PATCH 10/10] Fixed regression in update_ram tests --- src/update_ram.c | 8 ++++---- tools/unit-tests/unit-update-ram-nofixed.c | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/update_ram.c b/src/update_ram.c index 9d9655abb1..0ceef8d530 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -138,7 +138,7 @@ void RAMFUNCTION wolfBoot_start(void) uint8_t *dts_addr = NULL; uint32_t dts_size = 0; #endif -#ifndef ALLOW_DOWNGRADE +#if !defined(ALLOW_DOWNGRADE) && defined(WOLFBOOT_FIXED_PARTITIONS) int boot_v_raw = (int)wolfBoot_current_firmware_version(); int update_v_raw = (int)wolfBoot_update_firmware_version(); uint32_t boot_v = 0U; @@ -150,7 +150,7 @@ void RAMFUNCTION wolfBoot_start(void) if (update_v_raw >= 0) update_v = (uint32_t)update_v_raw; max_v = (boot_v > update_v) ? boot_v : update_v; -#endif +#endif /* !ALLOW_DOWNGRADE && WOLFBOOT_FIXED_PARTITIONS */ memset(&os_image, 0, sizeof(struct wolfBoot_image)); @@ -175,7 +175,7 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_panic(); break; } -#ifndef ALLOW_DOWNGRADE +#if !defined(ALLOW_DOWNGRADE) && defined(WOLFBOOT_FIXED_PARTITIONS) { uint32_t active_v = (active == PART_UPDATE) ? update_v : boot_v; if ((max_v > 0U) && (active_v < max_v)) { @@ -184,7 +184,7 @@ void RAMFUNCTION wolfBoot_start(void) break; } } -#endif +#endif /* !ALLOW_DOWNGRADE && WOLFBOOT_FIXED_PARTITIONS */ #if defined(WOLFBOOT_DUALBOOT) && defined(WOLFBOOT_FIXED_PARTITIONS) wolfBoot_printf("Trying %s partition at %p\n", diff --git a/tools/unit-tests/unit-update-ram-nofixed.c b/tools/unit-tests/unit-update-ram-nofixed.c index 343739742f..3551d93812 100644 --- a/tools/unit-tests/unit-update-ram-nofixed.c +++ b/tools/unit-tests/unit-update-ram-nofixed.c @@ -193,7 +193,7 @@ static int add_payload(uint8_t part, uint32_t version, uint32_t size) return 0; } -START_TEST(test_invalid_update_rollback_to_older_boot_is_denied) +START_TEST(test_invalid_update_falls_back_to_boot) { uint8_t bad_digest[SHA256_DIGEST_SIZE]; @@ -210,9 +210,9 @@ START_TEST(test_invalid_update_rollback_to_older_boot_is_denied) wolfBoot_start(); - ck_assert_int_eq(wolfBoot_staged_ok, 0); - ck_assert_int_eq(wolfBoot_panicked, 1); - ck_assert_uint_eq((uintptr_t)wolfBoot_stage_address, STAGE_ADDR_SENTINEL); + ck_assert_int_eq(wolfBoot_staged_ok, 1); + ck_assert_int_eq(wolfBoot_panicked, 0); + ck_assert_uint_eq((uintptr_t)wolfBoot_stage_address, WOLFBOOT_LOAD_ADDRESS); cleanup_flash(); } END_TEST @@ -222,7 +222,7 @@ 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_rollback_to_older_boot_is_denied); + tcase_add_test(tc, test_invalid_update_falls_back_to_boot); tcase_set_timeout(tc, 5); suite_add_tcase(s, tc);