Block rollback fallback in non-flash update paths

F/2254
pull/751/head
Daniele Lacamera 2026-04-15 17:31:29 +02:00
parent 91e0b16788
commit 1359aac80d
7 changed files with 79 additions and 14 deletions

View File

@ -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 */
}

View File

@ -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;

View File

@ -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)

View File

@ -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",

View File

@ -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;

View File

@ -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);

View File

@ -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);