unit-tests: pin the update-trigger scrub payload and ordering

The hal_flash_write stub discarded the written data, so the test
only checked call counts and the final NVM_CACHE state.  An
implementation that scrubbed before the write - programming an
all-zero flags sector and destroying the firmware key that persists
in the trailer - would have satisfied every assertion.

Capture the written sector in the stub at call time and assert the
staged payload: the sector fill and key pattern intact, the fresh
IMG_STATE_UPDATING byte and magic in place, with NVM_CACHE still
required to be zero afterwards.  Mutation-checked: a scrub-before-
write variant that programs the zeroed sector fails the payload
assertion.
pull/874/head
Daniele Lacamera 2026-08-26 20:10:47 +02:00
parent 2fdc99f323
commit 42a42ec1c1
1 changed files with 27 additions and 4 deletions

View File

@ -12,8 +12,12 @@
*
* The real function is extracted by the Makefile (together with
* nvm_cache_scrub()) and run with a test-owned NVM_CACHE, a staged
* sector carrying a key pattern and stubbed flash calls; the buffer
* must be zero after the call.
* sector carrying a key pattern and stubbed flash calls. The stub
* captures the written sector: it must carry the staged payload
* (key pattern plus the fresh flags), which pins the scrub-after-
* write ordering - a scrub that ran first would program an
* all-zero sector and destroy the key that persists in the trailer
* - and NVM_CACHE must be zero after the call.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
@ -66,14 +70,18 @@ static uint8_t g_sector[NVM_CACHE_SIZE]
* g_sector (the alignment above makes that true by construction). */
#define PART_UPDATE_ENDFLAGS ((uintptr_t)(g_sector + WOLFBOOT_SECTOR_SIZE))
/* Stubbed flash layer: records calls. */
/* Stubbed flash layer: records calls and captures the written
* sector at call time, before the function under test can scrub it. */
static int g_flash_writes;
static int g_flash_erases;
static uint8_t g_written[WOLFBOOT_SECTOR_SIZE];
int hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
(void)address; (void)data; (void)len;
ck_assert_int_eq(len, WOLFBOOT_SECTOR_SIZE);
memcpy(g_written, data, WOLFBOOT_SECTOR_SIZE);
g_flash_writes++;
(void)address;
return 0;
}
@ -151,6 +159,8 @@ static void teardown(void)
* before rewriting the flags. Pre-fix the staged pattern remained. */
START_TEST(test_update_trigger_scrubs_cache)
{
uint32_t magic = WOLFBOOT_MAGIC_TRAIL;
ck_assert_int_eq(cache_scrubbed(), 1);
wolfBoot_update_trigger();
@ -159,6 +169,19 @@ START_TEST(test_update_trigger_scrubs_cache)
* both candidate sectors */
ck_assert_int_eq(g_flash_writes, 1);
ck_assert_int_eq(g_flash_erases, 2);
/* The written sector carries the staged payload: the sector fill
* and key pattern intact, the fresh state and magic in place.
* A scrub before the write would have programmed all zeros and
* destroyed the key that persists in the trailer. */
ck_assert_int_eq(g_written[0], 0x11);
ck_assert_int_eq(g_written[KEY_OFF], 0xA5);
ck_assert_int_eq(g_written[KEY_OFF + KEY_LEN - 1], 0xA5);
ck_assert_int_eq(g_written[SECTOR_FLAGS_SIZE], IMG_STATE_UPDATING);
ck_assert_mem_eq(g_written + SECTOR_FLAGS_SIZE + 1, &magic,
sizeof(magic));
/* and the RAM copy of that sector is gone */
ck_assert_int_eq(cache_scrubbed(), 1);
}
END_TEST