diff --git a/src/libwolfboot.c b/src/libwolfboot.c index f5161446..99db6ee9 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 244d9115..bb729122 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 65ce6391..b0725404 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 24729906..902f1ad4 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 2e7831de..80d00c43 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 39d047f9..1b936a98 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 fecac505..2837629f 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);