Condense the verbose comments

The test file headers ran to 20-40 lines of prose before the licence,
restating the whole finding and the harness design. Cut them to a short
paragraph on what broke and a short one on how the test reaches the
code. Same for the long inline blocks in the HAL and libwolfboot
changes.

Comments only; no functional change.
pull/862/head
Daniele Lacamera 2026-08-18 20:12:06 +02:00
parent 640ee478e4
commit 502a69e9df
24 changed files with 207 additions and 392 deletions

View File

@ -678,12 +678,10 @@ static int hal_fman_init(void)
return -1;
}
/* Validate the container before uploading (the T10xx
* qe_check_firmware() path checks the same things): version,
* microcode count, self-consistent length, every code range inside
* the declared image, and the whole image inside the remaining NOR
* bank. 64-bit arithmetic so the sums cannot wrap. Fail closed:
* FMan stays unconfigured on any mismatch. */
/* Validate before uploading, as the T10xx qe_check_firmware() path
* does: version, count, self-consistent length, every code range
* inside the image, the image inside the NOR bank. 64-bit so the
* sums cannot wrap; FMan stays unconfigured on any mismatch. */
if (hdr->version != 1) {
wolfBoot_printf("FMAN: version %d unsupported\n", hdr->version);
return -1;

View File

@ -246,9 +246,8 @@ static int open_kernel_image(EFI_FILE_HANDLE vol, CHAR16 *filename,
if (file == NULL)
return -1;
/* FileSize() is 64-bit; the rest of the loader (header parsing, LoadImage
* size) works in uint32_t. Reject a size that would not fit rather than
* silently truncating it into *sz. */
/* FileSize() is 64-bit but the loader works in uint32_t; reject a
* size that would not fit rather than truncating it. */
filesz = FileSize(file);
if (filesz == 0 || filesz > 0xFFFFFFFFULL) {
wolfBoot_printf("Invalid file size: 0x%lx\n", (unsigned long)filesz);
@ -273,12 +272,9 @@ static int open_kernel_image(EFI_FILE_HANDLE vol, CHAR16 *filename,
return -1;
}
/* EFI_FILE_PROTOCOL.Read() takes a UINTN *BufferSize (64 bits on
* x86-64) and writes the bytes actually read through it. Passing the
* caller's uint32_t *sz would let an ordinary read store eight bytes
* through a four-byte stack object, and the 64-bit read of
* *BufferSize would see adjacent stack bytes. Same contract as the
* AArch64 sibling. */
/* Read() takes a UINTN *BufferSize (64-bit here) and writes the
* byte count back through it; passing the caller's uint32_t *sz
* would store eight bytes through a four-byte object. */
readsz = (UINTN)*sz;
status = uefi_call_wrapper(file->Read, 3, file, &readsz,
(void *)(uintptr_t)*_addr);

View File

@ -58,23 +58,18 @@ extern int tolower(int c);
#endif
#ifdef USE_FAST_MATH
/* WC_NO_HARDEN is intended only for verify-only builds: the image
* verification path performs public-key operations only, so timing
* resistance and hardening are not required there. Software DICE
* (WOLFCRYPT_TZ_PSA without WOLFBOOT_DICE_HW) is not verify-only:
* it signs the attestation claims with the private IAK via
* wc_CoseSign1_Sign_ex() and must keep the timing hardening, so
* exclude it. Hardware DICE (WOLFBOOT_DICE_HW) keeps the signing
* in the crypto engine and stays verify-only in fast math. */
/* WC_NO_HARDEN suits verify-only builds, which do public-key
* operations only. Software DICE (WOLFCRYPT_TZ_PSA without
* WOLFBOOT_DICE_HW) signs the attestation claims with the private
* IAK, so it is excluded; hardware DICE keeps signing in the crypto
* engine and stays verify-only. */
# if !defined(WOLFCRYPT_TZ_PSA) || defined(WOLFBOOT_DICE_HW)
# define WC_NO_HARDEN
# else
/* Turn the hardening on rather than only dropping
* WC_NO_HARDEN: tfm.c never tests WC_NO_HARDEN, so removing it
* changes no code -- it only un-silences the advisory in
* settings.h, which -Werror then turns into a build failure.
* TFM_TIMING_RESISTANT is what actually makes tfm.c constant
* time (ECC_TIMING_RESISTANT is already set with HAVE_ECC). */
/* tfm.c never tests WC_NO_HARDEN, so dropping it alone changes
* no code and only un-silences an advisory that -Werror turns
* into a build failure. TFM_TIMING_RESISTANT is what makes
* tfm.c constant time. */
# define TFM_TIMING_RESISTANT
# endif
#endif

View File

@ -256,14 +256,11 @@ static const uint32_t wolfboot_magic_trail = WOLFBOOT_MAGIC_TRAIL;
static uint8_t NVM_CACHE[NVM_CACHE_SIZE] XALIGNED(16);
static int nvm_cached_sector = 0;
/* Scrub the staging buffer without depending on wolfCrypt. ForceZero()
* would drag <wolfssl/wolfcrypt/types.h> and <wolfcrypt/src/misc.c>
* into every NVM_FLASH_WRITEONCE build of this file, including the two
* that cannot supply them: tools/check_config (no wolfSSL include
* path) and the STM32Cube test-app (no stm32*_hal_conf.h). A volatile
* byte loop is also the right shape for a RAMFUNCTION caller, since
* ForceZero() itself lives in flash and must not be called while the
* flash is being programmed. */
/* Scrub the staging buffer without depending on wolfCrypt: ForceZero()
* would pull wolfSSL headers into every NVM_FLASH_WRITEONCE build,
* including tools/check_config and the STM32Cube test-app, which cannot
* supply them. A volatile byte loop also suits the RAMFUNCTION callers,
* since ForceZero() lives in flash. */
static void RAMFUNCTION nvm_cache_scrub(void)
{
volatile uint8_t *p = (volatile uint8_t *)NVM_CACHE;

View File

@ -1541,15 +1541,11 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr,
}
}
/* Check for errors.
*
* An earlier failure (e.g. the SDMA wait above timing out) must survive
* this block. A wolfBoot-side wait timeout does not necessarily set an
* SRS12 error bit, so without preserving `status` the CMD12 / wait-busy
* results below would overwrite it and sdhci_transfer() would report
* success for a transfer that never completed. The caller then uses a
* partially filled buffer, which surfaces much later as a bogus image
* integrity failure rather than as the I/O error it actually is. */
/* Check for errors. An earlier failure (e.g. the SDMA wait timing
* out) must survive this block: a wolfBoot-side timeout need not set
* an SRS12 error bit, so without preserving `status` the CMD12 and
* wait-busy results below would report success for a transfer that
* never completed. */
reg = SDHCI_REG(SDHCI_SRS12);
if ((reg & SDHCI_SRS12_ERR_STAT) == 0) {
/* If multi-block, send CMD12 to stop transfer. This is issued even

View File

@ -1,23 +1,16 @@
/* unit-aurix-erased-fill.c
*
* Regression test: cacheSector() and
* programBytesToErasedFlash() in hal/aurix_tc3xx.c filled their
* uint32_t page buffers with the byte-valued FLASH_BYTE_ERASED, so in
* the default (non-inverted) build every 32-bit word became
* 0x000000FF instead of 0xFFFFFFFF. A one-byte write into an
* erased page then programmed zeroes into the unrelated bytes of the
* same word, and the sector-cache path corrupted logically-erased
* pages during read-modify-write.
*
* The test extracts the real cacheSector() and
* programBytesToErasedFlash() from hal/aurix_tc3xx.c (generated by
* the Makefile) and runs them against an emulated tc3_flash_* API on
* a host flash array. The assertions use FLASH_BYTE_ERASED /
* FLASH_WORD_ERASED from include/wolfboot/wolfboot.h, so the same
* source is built twice: default polarity (erased = 0xFF, where the
* pre-fix bug shows) and WOLFBOOT_FLAGS_INVERT (erased = 0x00, where
* the fix must not change the fill value).
* Regression test: cacheSector() and programBytesToErasedFlash() in
* hal/aurix_tc3xx.c filled uint32_t page buffers with the byte-valued
* FLASH_BYTE_ERASED, so each word became 0x000000FF instead of
* 0xFFFFFFFF. A one-byte write into an erased page then programmed
* zeroes into the rest of the word.
*
* Extracts both functions (generated by the Makefile) and runs them
* against an emulated tc3_flash_* API. Built twice, once per flag
* polarity: default (erased = 0xFF), where the bug shows, and
* WOLFBOOT_FLAGS_INVERT (erased = 0x00), where the fill must not
* change.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,23 +1,17 @@
/* unit-efi-x86-open-image.c
*
* Regression test: open_kernel_image() in hal/x86_64_efi.c passed
* the caller's uint32_t *sz directly as the BufferSize argument of
* EFI_FILE_PROTOCOL.Read(). That parameter is a UINTN, which is 64 bits on
* x86-64, and the UEFI spec requires the firmware to write the number of
* bytes actually read back through it. An ordinary successful read therefore
* stored eight bytes through the four-byte object, corrupting the stack bytes
* immediately after kernel_size/update_size in efi_main on every image load.
*
* The fix passes a local UINTN to Read() and copies the completed byte count
* back into *sz, exactly as the AArch64 sibling (hal/aarch64_efi.c) does.
*
* hal/x86_64_efi.c is normally only built by the CMake x86_64_efi target
* (gnu-efi headers, -lgnuefi -lefi), so this test doubles as its only host
* build coverage: it includes the HAL file directly and stands in for the
* gnu-efi runtime with host mocks (BS, LibFileInfo, the x86-64 efi_callN()
* trampolines, wolfBoot_start). The mock Read() follows the spec'd BufferSize
* contract, so this test fails against the pre-fix code.
* Regression test: open_kernel_image() in hal/x86_64_efi.c passed the
* caller's uint32_t *sz as the BufferSize argument of
* EFI_FILE_PROTOCOL.Read(). That is a UINTN -- 64 bits on x86-64 --
* and the firmware writes the byte count back through it, so every
* successful read stored eight bytes through a four-byte object and
* corrupted the stack after it. The fix passes a local UINTN and copies
* the count back, as the AArch64 sibling does.
*
* hal/x86_64_efi.c is normally built only by the CMake x86_64_efi
* target, so this doubles as its host build coverage: it includes the
* HAL directly and mocks the gnu-efi runtime. The mock Read() follows
* the spec'd BufferSize contract, so it fails against the pre-fix code.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,28 +1,17 @@
/* unit-ls1028a-xspi-write.c
*
* Regression test: hal_flash_write() and ext_flash_write() in
* hal/nxp_ls1028a.c issue xspi_write_en() once before xspi_flash_write(),
* but xspi_flash_write() issues one Page Program command per 256-byte
* chunk when len > XSPI_IP_BUF_SIZE. NOR flash clears its
* write-enable latch after each program operation, so with a single WEN
* only the first page of the request is actually programmed; the
* remaining Page Programs are rejected by the flash while the wrapper
* still reports success.
*
* The fix issues Write Enable for every page inside xspi_flash_write(),
* after the prior program has completed and before the Page Program
* command.
*
* The LS1028A XSPI driver is plain C register access apart from a few
* bare `asm volatile("...")` barrier statements (DDR init, delay), so
* the test includes the HAL file via nxp_ls1028a_host.c (generated by
* the Makefile, identical except those statements are blanked out),
* rebases XSPI_BASE onto host memory, and emulates the FlexSPI
* controller at the XSPI_IPCMD_START() boundary. The emulated NOR keeps
* a write-enable latch exactly like real NOR: set by WEN, consumed by
* each Page Program / Sector Erase, and a program or erase attempted
* without WEN is rejected.
* Regression test: hal_flash_write()/ext_flash_write() in
* hal/nxp_ls1028a.c issued one Write Enable before xspi_flash_write(),
* which programs a page per chunk. NOR clears the write-enable latch
* after each program, so only the first page landed while the wrapper
* still reported success. The fix enables writes for every page.
*
* The HAL is plain register access apart from a few barrier asm
* statements, so the test includes it via nxp_ls1028a_host.c (generated
* by the Makefile with those blanked), rebases XSPI_BASE onto host
* memory, and emulates the controller at the XSPI_IPCMD_START()
* boundary. The emulated NOR keeps a real write-enable latch and a busy
* window, so a dropped WEN or a missing wait shows up as a rejection.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,24 +1,15 @@
/* unit-nvm-cache-scrub.c
*
* Regression test: under NVM_FLASH_WRITEONCE the
* partition-trailer read-modify-write helpers stage a full flash
* sector into the file-scope buffer NVM_CACHE. In EXT_ENCRYPTED
* builds that same sector is where the firmware key/nonce live
* (ENCRYPT_CACHE is a macro alias of NVM_CACHE in this
* configuration), and neither trailer_write() nor
* partition_magic_write() scrubbed NVM_CACHE on return - the only
* ForceZero of the buffer is compiled out precisely for
* NVM_FLASH_WRITEONCE. A normal boot therefore leaves the plaintext
* key/nonce in .bss at a fixed address across the handoff to the
* Regression test: under NVM_FLASH_WRITEONCE the trailer helpers stage
* a whole flash sector into NVM_CACHE, which in EXT_ENCRYPTED builds is
* also where the firmware key and nonce live (ENCRYPT_CACHE aliases it).
* Neither trailer_write() nor partition_magic_write() scrubbed it on
* return, leaving the plaintext key in .bss across the handoff to the
* application.
*
* The test extracts the real trailer_write() and
* partition_magic_write() from src/libwolfboot.c (generated by the
* Makefile) and runs them with a test-owned NVM_CACHE, a staged
* sector image carrying a key/nonce pattern, and stubbed
* hal_flash_write/erase. After each call the whole buffer must be
* scrubbed; pre-fix the staged sector (key bytes included) remained.
*
* Extracts the real helpers (generated by the Makefile) and runs them
* with a test-owned NVM_CACHE, a staged sector carrying a key pattern
* and stubbed flash calls; the buffer must be zero after each call.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -3,41 +3,17 @@
* Regression tests for the FCM byte helpers and page loops in
* hal/nxp_p1021.c.
*
* hal_flash_read_bytes() and hal_flash_write_bytes() loop on
* `while (flash_idx < len)`, comparing the FCM buffer index (which
* hal_flash_set_addr() initializes to the page column, `flash_idx = col`)
* against the caller's relative byte count. With col != 0:
*
* - if len <= col the loop runs zero times, so the page program proceeds
* with stale FCM buffer contents (write) or the caller's buffer is left
* unchanged (read);
* - if len > col only len - col bytes are copied;
* - a final one-to-three byte remainder is copied as a full word, reading
* or writing outside the requested range.
*
* The fix computes an absolute end (flash_idx + len) and copies exactly
* len bytes, handling the tail byte-wise.
*
* the ext_flash_write()/ext_flash_read() page loops size every
* iteration from the total request length instead of the remaining
* length, do not cap it to the page, and program ELBC_FBCR with the
* column offset - so the byte-count field (FBCR[BC], P1021RM 12.3.30
* bits 20-31 MSB-first, i.e. the low 12 bits) carries the column rather
* than a count: a nonsense transfer size whenever col is non-zero.
* The fix uses chunk = min(len - pos, page_size - col), programs the BC
* field (0 only for a full page from column 0 - the only
* ECC-generating/checking setting) and advances by chunk.
*
* hal/nxp_p1021.c is PowerPC code, but the FCM helpers under test are
* plain byte copies. The test includes the HAL file (via nxp_p1021_host.c,
* generated by the Makefile) and drives the static helpers directly, with
* flash_buf backed by a host array standing in for the eLBC FCM region.
* The host copy differs from the real file only in that the three bare
* `__asm__ __volatile__("..." )` cache-barrier statements (DDR init and
* multi-core boot) are blanked out: they cannot assemble on x86. The
* PowerPC-asm accessors from nxp_ppc.h are shadowed with host versions
* below, so they are never emitted either.
* The helpers looped on `flash_idx < len`, comparing the FCM buffer
* index (which starts at the page column) against a relative byte
* count, so with col != 0 they copied too few bytes and rounded the
* tail up to a whole word. The page loops sized every iteration from
* the total length instead of the remainder, did not cap to the page,
* and programmed FBCR with the column instead of a byte count.
*
* The HAL is PowerPC but the helpers are plain byte copies, so the test
* includes it via nxp_p1021_host.c (generated by the Makefile, with the
* cache-barrier asm blanked) and drives the statics directly against a
* host array standing in for the FCM region.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,26 +1,18 @@
/* unit-p1021-qe-firmware.c
*
* Regression test (bounds part): qe_upload_firmware() in
* hal/nxp_p1021.c validated only the self-declared consistency of the
* QE microcode blob (magic, version, count range, length == computed
* size, optional CRC32). The per-microcode code_offset/count fields and
* the total length were never bounded to the 64 KiB buffer actually
* read from NAND (which, on the shipped p1021 config, sits in the
* update partition). A structurally consistent blob with a large
* code_offset or word count made the upload loop read (and copy into
* QE instruction RAM) arbitrarily far past the buffer.
* hal/nxp_p1021.c checked only the blob's self-consistency (magic,
* version, count, length, optional CRC32). The per-microcode
* code_offset/count and the total length were never bounded to the
* 64 KiB buffer read from NAND, so a structurally valid blob could make
* the upload loop read arbitrarily far past it.
*
* Note: the full recommendation also calls for cryptographic
* authentication of the microcode before activation. That is a design
* decision (keys, signed manifest, build flow) beyond a minimal patch
* and is tracked as a note on the finding; this test covers the
* fail-closed bounding that can be fixed in the code.
*
* The test compiles the real hal/nxp_p1021.c (as nxp_p1021_host.c,
* generated by the Makefile) with the PowerPC accessors shadowed by
* host equivalents, and drives qe_upload_firmware() directly with
* crafted blobs.
* Authenticating the microcode before activation is a separate design
* question (keys, signed manifest, build flow); this covers only the
* fail-closed bounding.
*
* Compiles the real HAL via nxp_p1021_host.c with the PowerPC accessors
* shadowed, and drives qe_upload_firmware() with crafted blobs.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,20 +1,16 @@
/* unit-sdhci-dma-error.c
*
* Regression test: a failed SDMA data transfer must be reported as an error.
*
* sdhci_transfer() waits for Transfer Complete on the SDMA path and sets
* status = -1 if that wait times out. The post-transfer "check for errors"
* block then runs unconditionally, and a wolfBoot-side wait timeout does not
* necessarily leave an error bit set in SRS12. Before the fix, the CMD12
* (stop transmission) result overwrote that -1, so the function returned
* success for a transfer that never moved any data.
*
* The caller then treats a partially filled buffer as a good read. On a
* verified-boot target that surfaces much later as an image integrity
* failure rather than as the I/O error it actually is -- and where
* anti-rollback is enabled, the good lower-versioned slot is refused too,
* leaving nothing bootable.
* Regression test: a failed SDMA data transfer must be reported as an
* error. sdhci_transfer() sets status = -1 when the Transfer Complete
* wait times out, but the error-check block that follows runs
* unconditionally and a wolfBoot-side timeout need not leave an error
* bit in SRS12, so the CMD12 result overwrote the -1 and the function
* returned success for a transfer that moved nothing.
*
* The caller then treats a partially filled buffer as a good read,
* which surfaces later as an integrity failure rather than the I/O
* error it is -- and with anti-rollback on, the good lower-versioned
* slot is refused too.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,26 +1,12 @@
/* unit-sdhci-uhs-recover.c
*
* Regression test: disk_read() called sdhci_uhs_recover()
* after any nonzero status from a data transfer, which permanently
* switched the host to 1.8V signaling (SDHCI_SRS15_V18SE) on a plain
* 3.3V cold boot whenever the first transfer failed for any reason
* (CRC, DMA, media, controller). If the retry also failed, the voltage
* setting was never rolled back and g_uhs_recovered prevented further
* recovery - so a transient error left the host with a signaling level
* the card is not using, for the rest of the boot and the stage that
* follows it.
*
* The test compiles the real src/sdhci.c (as sdhci_host.c, generated by
* the Makefile). The generated copy differs from the real file only in:
* - three `asm volatile` barrier/nop statements blanked (cannot
* assemble on x86);
* - the real sdhci_read() renamed to sdhci_read_hw() so this file can
* provide a scripted sdhci_read() that stands in for the command /
* data-transfer state machine.
* Register access goes through the platform-provided sdhci_reg_read()/
* sdhci_reg_write(), backed here by a host register array, so the test
* asserts directly on the SDHCI_SRS15 / SDHCI_SRS11 state.
* Regression test: disk_read() called sdhci_uhs_recover() on any read
* error, switching the host to 1.8V signaling permanently even when
* the retry did not help.
*
* The controller is scripted through a host register file and
* sdhci_read() returns a per-call result from a test-supplied script,
* so a chunk can fail, succeed on retry, or fail twice.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,29 +1,13 @@
/* unit-sdhci-wait-busy.c
*
* Regression test: sdhci_wait_busy() had no timeout in
* either its DATA0 polling loop or its repeated CMD13 loop (the
* in-code TODO acknowledged it). A removed card, a controller fault,
* or a card stuck in the programming state left wolfBoot spinning
* forever instead of returning an I/O error.
*
* The test compiles the real src/sdhci.c (as sdhci_host.c, generated
* by the Makefile: asm blanked, sdhci_read renamed to sdhci_read_hw)
* and scripts the controller through the host register file:
* - the DATA0 level (SDHCI_SRS09_DAT0_LVL) stays low for the
* data-line timeout case;
* - the command always "completes" (SDHCI_SRS12_CC preset) with the
* READY_FOR_DATA bit (bit 8) of the response register
* (SDHCI_SRS04) cleared, so every CMD13 reports DEVICE_BUSY.
* The emulated hal_get_timer_us() advances by a settable step per read,
* so the shipped SDHCI_WAIT_BUSY_TIMEOUT_MS is exercised as built --
* a 1 ms step reaches the 30 s budget in ~30k iterations -- rather than
* against a shrunken stand-in value. Pre-fix, both timeout cases never
* return (the test runner's timeout kills them).
*
* The stub also counts sdhci_platform_wdt_pet() calls: a bounded wait
* this long must service the watchdog or it becomes a reset rather than
* the clean I/O error it is meant to be.
* Regression test: sdhci_wait_busy() had no timeout in either the DAT0
* or the CMD13 loop, so a stuck or removed card spun forever.
*
* The timer stub advances by a settable step, so the shipped
* SDHCI_WAIT_BUSY_TIMEOUT_MS is exercised as built rather than against
* a shrunken stand-in, and it counts sdhci_platform_wdt_pet() calls: a
* wait this long must service the watchdog or it becomes a reset
* instead of the clean I/O error it is meant to be.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,27 +1,18 @@
/* unit-stm32l5-write.c
*
* Regression test: hal_flash_write() in hal/stm32l5.c
* looped while (i < len) but each iteration unconditionally read both
* 32-bit words of the 8-byte program unit (src[i>>2] and
* src[(i>>2)+1]) and programmed both, regardless of how many bytes
* remained. A write whose length is not a multiple of 8 therefore
* read up to 4 bytes past the caller's buffer (which the NSC update
* path validates for exactly len) and wrote those bytes to flash.
* Regression test: hal_flash_write() in hal/stm32l5.c read both words
* of the 8-byte program unit regardless of the remaining length, so a
* write not a multiple of 8 read up to 4 bytes past the caller's
* buffer and programmed them.
*
* The test extracts the real hal_flash_wait_complete(),
* hal_flash_clear_errors() and hal_flash_write() from hal/stm32l5.c
* (generated by the Makefile) and runs them with the FLASH_SR/CR
* registers on a host register file and the destination flash at a
* host memory address pre-filled with stale data. The source buffer
* is followed by a canary: pre-fix, a short write copies the canary
* into the destination flash; post-fix the partial final unit is
* read-modify-written -- bytes outside [i, len) come from the current
* flash content and are stored back unchanged -- so nothing past len
* is read or changed. Both words are always stored inside one PG
* window, because the flash has no 32-bit program mode; that is a
* hardware sequencing property this host model cannot observe, so it
* is asserted by construction in hal/stm32l5.c, not here.
* The fix read-modify-writes the whole unit: bytes outside [i, len)
* come from flash and go back unchanged. Both words are always stored
* in one PG window -- the flash has no 32-bit program mode -- which
* this host model cannot observe, so that is asserted by construction
* in the HAL, not here.
*
* Same harness as the STM32U5 twin: extracted functions, registers on
* a host file, stale destination flash, canary after the source.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,30 +1,20 @@
/* unit-stm32u5-write.c
*
* Regression test: hal_flash_write() in hal/stm32u5.c
* looped while (i < len) but each iteration unconditionally read all
* four 32-bit words of the 16-byte program unit (src[i>>2] through
* src[(i>>2)+3]) and programmed the full quadword regardless of the
* remaining length. A write whose length is not a multiple of 16
* therefore read up to 12 bytes past the caller's buffer (validated
* for exactly len by the NSC update path) and wrote those bytes to
* flash.
* Regression test: hal_flash_write() in hal/stm32u5.c read all four
* words of the 16-byte program unit regardless of the remaining
* length, so a write not a multiple of 16 read up to 12 bytes past the
* caller's buffer and programmed them.
*
* The fix read-modify-writes the whole 128-bit unit: bytes outside
* [i, len) come from the current flash content and are stored back
* unchanged, so the source is never read past len and no flash byte
* past len changes value. All four words are always stored, because
* the controller only starts the program on the fourth -- that part
* is a hardware sequencing property this host model cannot observe,
* so it is asserted by construction in hal/stm32u5.c, not here.
*
* The test extracts the real hal_flash_wait_complete(),
* hal_flash_clear_errors() and hal_flash_write() from hal/stm32u5.c
* (generated by the Makefile) and runs them with the FLASH_NS_SR/CR
* registers on a host register file and the destination flash at a
* 32-bit host address pre-filled with stale data. The source buffer
* is followed by a canary: pre-fix, a short write copies bytes past
* len into the destination flash.
* The fix read-modify-writes the whole unit: bytes outside [i, len)
* come from flash and go back unchanged. All four words are always
* stored -- the controller only starts the program on the fourth --
* which this host model cannot observe, so that part is asserted by
* construction in the HAL, not here.
*
* The real functions are extracted by the Makefile and run with the
* FLASH registers on a host register file and the destination flash
* pre-filled with stale data; a canary after the source buffer catches
* any read past len.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,19 +1,14 @@
/* unit-t10xx-dts-memac.c
*
* Regression test: the fsl,fman-memac loop in
* hal_dts_fixup() (hal/nxp_t10xx.c) read the node's cell-index
* straight from the device tree and used it to index the file-static
* phydevs[5] with no bounds check - unlike the qman-portal loop right
* above it. Standard NXP FMan device trees assign the 10G MACs
* cell-index 8/9, so any such DTB read past the array and wrote the
* result into the kernel device tree as a MAC address.
*
* The test compiles the real src/fdt.c (wolfBoot's libfdt subset) and
* the real hal_dts_fixup() (extracted by the Makefile from
* hal/nxp_t10xx.c, along with its liodn/qman static tables), feeds it
* hand-built DTBs, and checks the resulting local-mac-address
* properties.
* Regression test: the fsl,fman-memac loop in hal_dts_fixup()
* (hal/nxp_t10xx.c) took the node's cell-index straight from the device
* tree and indexed the static phydevs[5] with it, unlike the qman-portal
* loop above. NXP device trees give the 10G MACs cell-index 8/9, so such
* a DTB read past the array and wrote the result back as a MAC address.
*
* Compiles the real src/fdt.c and hal_dts_fixup() (extracted by the
* Makefile with its static tables), feeds it hand-built DTBs and checks
* the resulting local-mac-address properties.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,20 +1,16 @@
/* unit-t10xx-qe-firmware.c
*
* Regression test: qe_check_firmware() in hal/nxp_t10xx.c
* validated the QE microcode header for self-consistency (magic,
* version, count, length == computed size) but never bounded the
* Regression test: qe_check_firmware() in hal/nxp_t10xx.c validated the
* microcode header for self-consistency but never bounded the
* per-microcode code_offset against the declared image length.
* qe_upload_firmware() then reads code_offset + 4*count bytes from
* the firmware start and copies them into QE IRAM (and programs the
* microcode's trap table), so a structurally valid blob with an
* out-of-image offset makes wolfBoot copy arbitrary memory into the
* engine and run unauthenticated microcode with serial DMA enabled
* before the image is authenticated.
*
* The test extracts the real qe_* structures and qe_check_firmware()
* from hal/nxp_t10xx.c (generated by the Makefile) and builds the
* blobs in memory. The check-only path needs no register access.
* qe_upload_firmware() then copies code_offset + 4*count bytes into QE
* IRAM and programs the trap table, so a structurally valid blob with
* an out-of-image offset runs unauthenticated microcode with serial DMA
* enabled before the image is authenticated.
*
* Extracts the qe_* structures and qe_check_firmware() (generated by
* the Makefile) and builds the blobs in memory; the check-only path
* needs no register access.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,20 +1,15 @@
/* unit-t2080-fman-loader.c
*
* Regression test: hal_fman_init() in hal/nxp_t2080.c
* checked only the NOR window guard and the QEF magic, then copied
* fw->microcode[i].count words from fw + code_offset into FMan
* instruction RAM and marked the engine ready. No version, count,
* length self-consistency, or code-offset validation: a structurally
* valid-looking blob with an out-of-image offset made the loader
* copy arbitrary memory into FMan IRAM (and a count of zero was
* accepted as a successful "upload" of nothing).
*
* The test extracts the real qe_* structures and hal_fman_init() from
* hal/nxp_t2080.c (generated by the Makefile) and runs them against a
* test-owned NOR image (the FMAN slot sits at a nonzero offset in the
* bank, so the remaining-extent check is exercised) and an emulated
* FMan IRAM data register (set32/get32 on a host register array).
* Regression test: hal_fman_init() in hal/nxp_t2080.c checked only the
* NOR window guard and the QEF magic, then copied microcode words into
* FMan instruction RAM and marked the engine ready. With no version,
* count, length or code-offset validation, a valid-looking blob with an
* out-of-image offset copied arbitrary memory into IRAM, and a count of
* zero counted as a successful upload of nothing.
*
* Extracts the qe_* structures and hal_fman_init() (generated by the
* Makefile) and runs them against a test-owned NOR image, with the FMAN
* slot at a nonzero bank offset so the extent check is exercised.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,22 +1,16 @@
/* unit-ti-hercules-write.c
*
* Regression test: the short-write path of hal_flash_write()
* in hal/ti_hercules.c checked only len < WRITE_BLOCK_SIZE, not whether
* (address % WRITE_BLOCK_SIZE) + len stayed inside the block. A short
* write starting near the end of a block copied past the end of the
* WRITE_BLOCK_SIZE stack staging buffer (memcpy(temp + off, data, len))
* and programmed only the first block, losing the bytes intended for
* the next block.
*
* hal/ti_hercules.c needs the TI FAPI vendor headers (board build tree)
* and cannot be compiled on the host, but the bug is entirely in the
* staging logic of hal_flash_write(), so the Makefile extracts
* hal_flash_unlock_helper() and hal_flash_write() verbatim from
* hal/ti_hercules.c (ti_hercules_write_extract.h) and runs them here
* against emulated FAPI calls; Fapi_issueProgrammingCommand() performs
* the program into a host flash array, so the test asserts on the
* flash contents.
* Regression test: the short-write path of hal_flash_write() in
* hal/ti_hercules.c checked only len < WRITE_BLOCK_SIZE, not whether
* the write stayed inside the block. A short write near a block end
* copied past the staging buffer and programmed only the first block,
* losing the bytes meant for the next one.
*
* The HAL needs the TI FAPI vendor headers and cannot be built on the
* host, but the bug is in the staging logic, so the Makefile extracts
* hal_flash_write() and its unlock helper and runs them against an
* emulated FAPI. The emulated FSM stays busy for a few polls and holds
* the data back until it drains, so a missing wait is observable.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,23 +1,15 @@
/* unit-versal-ext-write.c
*
* Regression test: ext_flash_write() in hal/versal.c
* chunked the request into length-based FLASH_PAGE_SIZE transfers
* (page 0, page 1, ...) without accounting for the start address's
* offset inside the physical page. A write starting mid-page sent a
* full-page Page Program across the boundary; NOR wraps the write
* pointer at the page start, so the excess bytes clobbered the
* beginning of the page.
*
* hal/versal.c cannot be compiled on the host in its entirety for
* this test (the QspiDev_t typedef and the static qspi_* helpers are
* file-local), so the Makefile extracts the QspiDev_t struct and the
* real ext_flash_write() verbatim (versal_ext_write_extract.h) and
* runs them against test-owned qspiDev/qspi_initialized state and
* emulated qspi_write_enable()/qspi_transfer()/qspi_wait_ready()/
* qspi_write_disable(). The emulated NOR models the real wrap: a
* crossing program overwrites the page start it wraps into, so
* pre-fix the corruption is observable in the flash image.
* Regression test: ext_flash_write() in hal/versal.c chunked by length
* alone, ignoring where the start address sat inside the physical page.
* A write starting mid-page sent a full-page Page Program across the
* boundary, and NOR wraps the write pointer, so the excess clobbered
* the start of the page.
*
* The QspiDev_t typedef and the qspi_* helpers are file-local, so the
* Makefile extracts the struct and ext_flash_write() verbatim and runs
* them against emulated qspi_* calls. The emulated NOR models the wrap,
* so pre-fix the corruption is visible in the flash image.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,25 +1,15 @@
/* unit-versal-qspi-dma.c
*
* Regression test: the DMA RX path of qspi_transfer() in
* hal/versal.c. For an unaligned destination or a length not divisible
* by four, the transfer runs through the 4096-byte dma_tmpbuf. If the
* requested rxLen exceeded the temp buffer, dmaLen was truncated to
* sizeof(dma_tmpbuf) (so only that much was DMA'd), but the code then
* copied the full rxLen out with memcpy(rxData, dmaPtr, rxLen) - reading
* past the end of dma_tmpbuf and reporting the whole read as successful.
*
* The test compiles the real hal/versal.c (as versal_host.c, generated
* by the Makefile). The generated copy differs from the real file only
* in:
* - ARM asm statements blanked (cannot assemble on x86; the cache
* maintenance helpers become no-ops, which is harmless because the
* DMA is emulated below);
* - VERSAL_QSPI_BASE rebased onto g_vqspi_regs (real register
* offsets are preserved);
* - the real qspi_dma_wait() renamed to qspi_dma_wait_hw() so this
* file can provide a qspi_dma_wait() that performs the DMA move the
* hardware would do (flash -> GQSPIDMA_DST, GQSPIDMA_SIZE bytes).
* Regression test: the DMA RX path of qspi_transfer() in hal/versal.c.
* An unaligned or non-multiple-of-four read goes through a 4096-byte
* temp buffer; when rxLen exceeded it, dmaLen was truncated but the
* copy out still used the full rxLen, reading past the buffer and
* reporting success.
*
* Compiles the real HAL via versal_host.c (generated by the Makefile),
* which differs only in that ARM asm is blanked or rewritten, the QSPI
* base is rebased onto a host array, and qspi_dma_wait() is renamed so
* this file can supply one that performs the DMA move.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,21 +1,15 @@
/* unit-zynq-erase-loop.c
*
* Regression test: ext_flash_erase() in hal/zynq.c initializes
* idx once before the multi-sector while (len > 0) loop and never resets
* it, even though cmd is memset to zero each iteration. From the second
* sector on, the erase command is written at cmd[idx] (past the start of
* the buffer): in three-byte address mode the flash receives leading zero
* bytes before the opcode (malformed command, erase silently fails); in
* four-byte address mode the writes run past the end of the 8-byte cmd
* buffer. The loop also kept going - and could return success - after a
* failed write-enable, transfer, or ready wait.
*
* hal/zynq.c cannot be compiled on the host (it needs the Xilinx SDK
* headers, which are provided by the board build tree), but the bug is
* entirely in the ext_flash_erase() loop, so the Makefile extracts that
* one function verbatim from hal/zynq.c (zynq_erase_extract.h) and runs
* it here against emulated qspi_* functions that record every operation.
* Regression test: ext_flash_erase() in hal/zynq.c set idx once before
* the multi-sector loop and never reset it, though cmd is cleared each
* iteration. From the second sector on the opcode landed at cmd[idx]:
* malformed commands in three-byte address mode, writes past the 8-byte
* buffer in four-byte mode. The loop also continued, and could return
* success, after a failed write-enable, transfer or ready wait.
*
* hal/zynq.c needs the Xilinx SDK headers and cannot be built on the
* host, so the Makefile extracts the one function and runs it against
* emulated qspi_* calls that record every operation.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.

View File

@ -1,20 +1,15 @@
/* unit-zynq-ext-write.c
*
* Regression test: ext_flash_write() in hal/zynq.c divided
* the request into length-based FLASH_PAGE_SIZE chunks (page 0, page
* 1, ...) without accounting for the start address's offset inside
* the device page. A write starting mid-page sent a full-page Page
* Program across the boundary; NOR wraps the write pointer at the
* page start, so the excess bytes clobbered the beginning of the
* page.
*
* As in unit-zynq-erase-loop, hal/zynq.c cannot be compiled on the
* host (Xilinx SDK headers), so the Makefile extracts ext_flash_write()
* verbatim (zynq_write_extract.h) and runs it against emulated
* qspi_* functions. The emulated NOR models the real wrap: a Page
* Program crossing the boundary overwrites the page start it wraps
* into, so pre-fix the corruption is observable in the flash image.
* Regression test: ext_flash_write() in hal/zynq.c chunked by length
* alone, ignoring the start address's offset inside the device page. A
* write starting mid-page sent a full-page Page Program across the
* boundary, and NOR wraps the write pointer, so the excess clobbered
* the start of the page.
*
* As with unit-zynq-erase-loop the HAL cannot be built on the host, so
* the Makefile extracts ext_flash_write() and runs it against emulated
* qspi_* calls. The emulated NOR models the wrap, so pre-fix the
* corruption is visible in the flash image.
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.