Skip to content
5 changes: 5 additions & 0 deletions .github/workflows/test-hooks-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-sunnyday-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
danielinux marked this conversation as resolved.

- name: Run dualbank swap simulation
run: |
tools/scripts/sim-dualbank-swap-update.sh
Expand Down
9 changes: 9 additions & 0 deletions src/libwolfboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Comment thread
danielinux marked this conversation as resolved.
return candidate;
}
Comment thread
danielinux marked this conversation as resolved.
#endif
wolfBoot_erase_partition(candidate);
candidate ^= 1; /* switch to other partition if available */
}
Expand Down
23 changes: 21 additions & 2 deletions src/update_disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,14 @@ 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;
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;
Expand Down Expand Up @@ -345,10 +347,16 @@ void RAMFUNCTION wolfBoot_start(void)
wolfBoot_panic();
}

wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver, pB_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();
Expand All @@ -368,6 +376,16 @@ void RAMFUNCTION wolfBoot_start(void)
cur_part = BOOT_PART_B;
else
cur_part = BOOT_PART_A;
#ifndef ALLOW_DOWNGRADE
{
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();
return;
}
}
#endif

part_name[2] = 'A' + selected;

Comment thread
danielinux marked this conversation as resolved.
Expand Down Expand Up @@ -498,6 +516,7 @@ void RAMFUNCTION wolfBoot_start(void)
#endif
wolfBoot_printf("Unable to find a valid partition!\r\n");
wolfBoot_panic();
return;
}

disk_close(BOOT_DISK);
Expand Down
22 changes: 22 additions & 0 deletions src/update_flash_hwswap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,12 +46,33 @@ void RAMFUNCTION wolfBoot_start(void)
int active;
struct wolfBoot_image fw_image;
uint8_t p_state;
#ifndef ALLOW_DOWNGRADE
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
Comment thread
danielinux marked this conversation as resolved.
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();
Comment thread
danielinux marked this conversation as resolved.
Comment thread
danielinux marked this conversation as resolved.
return;
}
#endif
if ((wolfBoot_open_image(&fw_image, active) < 0)
#ifndef WOLFBOOT_SKIP_BOOT_VERIFY
|| (wolfBoot_verify_integrity(&fw_image) < 0)
Expand Down
23 changes: 23 additions & 0 deletions src/update_ram.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ void RAMFUNCTION wolfBoot_start(void)
uint8_t *dts_addr = NULL;
uint32_t dts_size = 0;
#endif
#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;
uint32_t update_v = 0U;
uint32_t max_v = 0U;

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;
Comment thread
danielinux marked this conversation as resolved.
#endif /* !ALLOW_DOWNGRADE && WOLFBOOT_FIXED_PARTITIONS */

memset(&os_image, 0, sizeof(struct wolfBoot_image));

Expand All @@ -162,6 +175,16 @@ void RAMFUNCTION wolfBoot_start(void)
wolfBoot_panic();
break;
}
#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)) {
wolfBoot_printf("Rollback to lower version not allowed\n");
wolfBoot_panic();
break;
}
Comment thread
danielinux marked this conversation as resolved.
}
#endif /* !ALLOW_DOWNGRADE && WOLFBOOT_FIXED_PARTITIONS */

#if defined(WOLFBOOT_DUALBOOT) && defined(WOLFBOOT_FIXED_PARTITIONS)
wolfBoot_printf("Trying %s partition at %p\n",
Expand Down
1 change: 1 addition & 0 deletions tools/keytools/keygen.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
65 changes: 65 additions & 0 deletions tools/scripts/sim-dualbank-rollback-denied.sh
Original file line number Diff line number Diff line change
@@ -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."
2 changes: 1 addition & 1 deletion tools/tpm/policy_create.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
7 changes: 5 additions & 2 deletions tools/tpm/policy_sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -221,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 */
Expand Down
19 changes: 19 additions & 0 deletions tools/unit-tests/unit-update-disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -288,6 +292,20 @@ 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_gt(wolfBoot_panicked, 0);
ck_assert_int_eq(mock_do_boot_called, 0);
}
END_TEST

Suite *wolfboot_suite(void)
{
Suite *s = suite_create("wolfBoot");
Comment thread
danielinux marked this conversation as resolved.
Expand All @@ -297,6 +315,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;
Expand Down
11 changes: 7 additions & 4 deletions tools/unit-tests/unit-update-ram-nofixed.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_MAX

#include "user_settings.h"
#include "wolfboot/wolfboot.h"
Expand All @@ -52,7 +53,8 @@ int wolfBoot_dualboot_candidate_addr(void** addr)
#include <wolfssl/wolfcrypt/sha256.h>

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)
{
Expand Down Expand Up @@ -191,7 +193,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_falls_back_to_boot)
{
uint8_t bad_digest[SHA256_DIGEST_SIZE];

Expand All @@ -209,7 +211,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_panicked, 0);
ck_assert_uint_eq((uintptr_t)wolfBoot_stage_address, WOLFBOOT_LOAD_ADDRESS);
Comment thread
danielinux marked this conversation as resolved.
cleanup_flash();
}
END_TEST
Expand All @@ -219,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_falls_back_to_boot_without_reselect_loop);
tcase_add_test(tc, test_invalid_update_falls_back_to_boot);
tcase_set_timeout(tc, 5);
suite_add_tcase(s, tc);

Expand Down
Loading
Loading