Three CI failures from the new bootloader-region overlap guards:
renesas_rx72n: the swap check measured the swap area as a full
WOLFBOOT_PARTITION_SIZE, but swap is one sector (the pre-existing
boot/update-vs-swap guards already use WOLFBOOT_SECTOR_SIZE). On rx72n
that inflated swap to 0xFFFE0000+0x1F0000 and falsely overlapped the
write-protect region at 0xFFFF0000. Use WOLFBOOT_SECTOR_SIZE.
- Add missing END_TEST after each START_TEST in unit-nrf5340-uart-crlf.c
- Fix partition overlap checks in target.h.in to use standard interval
intersection (catches partitions that start below WOLFBOOT_ORIGIN and
extend into the bootloader region)
nrf5340: the target references nrf5340_uart_crlf (src/update_flash.c)
but the Makefile does not compile hal/nrf5340_uart.c, so the linker
fails. Add the object to OBJS (Makefile) and APP_OBJS (test-app).
cypsoc6: the new boot/update/swap vs bootloader-region #error guards
in include/target.h.in fire on the cypsoc6 layout, where the boot
partition intentionally overlaps the bootloader region (the bootloader
and the boot image share the same flash region). Introduce
WOLFBOOT_ALLOW_PART_OVERLAP (defined in cypsoc6.config, gated in
target.h.in, added to CONFIG_VARS in tools/config.mk) to suppress the
guards for targets that legitimately overlap.
Verified: nrf5340 builds (make distclean + cp config + make, exit 0,
wolfboot.bin created). cypsoc6 cannot be built locally (the CI checks
out lib/psoc6pdl from Infineon, which is not available locally), but
the WOLFBOOT_ALLOW_PART_OVERLAP guard is verified to suppress the
#error for the cypsoc6 layout.
The boot, update and swap partitions were never checked against the
bootloader write-protect region [WOLFBOOT_ORIGIN, WOLFBOOT_ORIGIN +
BOOTLOADER_PARTITION_SIZE). An over-sized BOOTLOADER_PARTITION_SIZE
silently write-protects the head of a partition; the existing
boot/update/swap overlap guards did not cover this.
Add three #error guards (boot, update, swap vs bootloader region) to
include/target.h.in, gated on WOLFBOOT_ORIGIN < the partition address so
they only fire for real targets (the mock target has WOLFBOOT_ORIGIN ==
WOLFBOOT_PARTITION_BOOT_ADDRESS, a valid simplified geometry).
The undersized guard (BOOTLOADER_PARTITION_SIZE not covering the
self-update erase range) is deferred: it requires a compile-time
constant for ARCH_FLASH_OFFSET, which is a runtime value (sim_ram_base)
for the mock target.
The compile-only negative tests (part 2 of the finding) are deferred.
The wolfSSL bump to master 4aa1ad7a5 wraps all of coding.h in
"#ifndef NO_CODING". That block also holds the auto-enable that turns
WOLFSSL_KEY_GEN into WOLFSSL_BASE64_ENCODE, so wolfBoot configurations
that set both WOLFSSL_KEY_GEN (SECURE_PKCS11) and NO_CODING lost the
Base64_Encode() declaration -- and coding.c compiles to nothing under
NO_CODING, so the definition is gone too. settings.h still derives
WOLFSSL_DER_TO_PEM from WOLFSSL_KEY_GEN, so asn.c kept compiling
wc_DerToPemEx() and failed with an implicit declaration of
Base64_Encode(). This broke every TrustZone and PKCS11 target.
wolfBoot never emits PEM, so define WOLFSSL_NO_DER_TO_PEM whenever
NO_CODING is set rather than pulling base64 back in. The check sits
after the HAVE_FIPS block, which may undef NO_CODING. wolfTPM's
wc_DerToPem() callers are themselves guarded by WOLFSSL_DER_TO_PEM, so
they simply drop their optional PEM encoding path.
Separately, the update grows wolfboot by 8-136 bytes depending on the
configuration, so re-measure every test-size-all entry with the CI
container and record the new sizes.
c3f9717f guarded the success/trigger/sector-flag declarations behind
WOLFBOOT_FIXED_PARTITIONS, but wolfboot.h includes target.h only under
__WOLFBOOT, so test-apps (compiled without __WOLFBOOT) never saw the
macro and failed with implicit-declaration errors on imx95_m7, mcxa,
mcxw and mcxw-tz. Unguard the header declarations; keep the definitions
guarded and move wolfBoot_success out of the fixed-partition guard
since it only uses the partition-state API.
Called from the RAM-resident update path (update_ram.c) under
EXT_FLASH; place the body in RAM so it runs after flash remap
instead of faulting.
Verification: built unit-custom-trailer-nopart (EXT_FLASH) - compiles
clean; imx95_m7 (EXT_FLASH, CI repro) - compiles clean.
- libwolfboot.c: keep the partition state APIs (set/get_partition_state)
available to CUSTOM_PARTITION_TRAILER / WOLFBOOT_NO_PARTITIONS builds;
guard the fixed-partition APIs (sector flags, erase, trigger, success)
behind WOLFBOOT_FIXED_PARTITIONS. Add the unit-custom-trailer-nopart
compile target to lock in that configuration.
- unit-p1021-fcm-bytes.c: add program tests for status 0x81 (fail) and
0x00 (write-protected), asserting no later page is programmed.
- unit-p1021-erase-advance.c: add an erase test for status 0x00
(write-protected), asserting failure before advancing to the next block.
Add HAVE_PARTITION_TRAILERS macro (set when WOLFBOOT_FIXED_PARTITIONS or
CUSTOM_PARTITION_TRAILER is defined) and use it to gate the TESTING state
transition in update_ram.c. Previously the gate was WOLFBOOT_FIXED_PARTITIONS
only, so targets using custom partition trailers never transitioned from
UPDATING to TESTING, allowing rollback to older versions.
Add unit-update-ram-custom-trailer test covering the CUSTOM_PARTITION_TRAILER
path with mocked get/set_trailer_at callbacks.
Secure-mode worlds (TZ_PSA/PKCS11/FWTPM/WOLFHSM) process private keys
in software, so a USE_FAST_MATH build of one of them must keep the
timing-resistant TFM path instead of falling back to WC_NO_HARDEN.
The old condition only covered software DICE; FIPS=1 already forces
SPMATH (options.mk M10) and all CMake presets set SPMATH, so no
shipped config changes - this closes the integrator Makefile gap.
WOLFBOOT_DICE_HW stays verify-only (requires WOLFCRYPT_TZ_PSA).
wolfBoot_start() zeroed os_image once, before the retry loop, so the
fallback iteration kept the stale img->hdr of the failed partition and
wolfBoot_open_image_address() (which only adopts load_address when
hdr is NULL) re-verified the wrong image. The external header cache
had the same problem: it kept the first image opened.
memset the struct and invalidate the EXT_FLASH header cache at the
top of each loop iteration, per the documented precondition of
wolfBoot_open_image_address().
Add unit-update-ram-nofixed-noramboot: the non-fixed-partition,
non-RAMBOOT (XIP) layout where load_address varies per retry, with
fallback tests both directions plus candidate-selection checks.
The store commits sectors with hal_flash_erase()/hal_flash_write() and
then reads them back through the memory map - sector_ptr(),
cache_get_sector()'s refill, and the raw magic reads in check_vault().
On a part that caches flash reads (STM32 ICACHE) those reads can return
pre-erase bytes. check_vault() is the worst case: a stale magic there
does not merely read wrong, it triggers restore_backup() or a full vault
re-initialisation, losing the token.
Every wolfPKCS11 field write flushed the payload sector and the header
sector to flash (2 erases + 2 programs of a full sector each), and the
token store re-serializes all objects per C_CreateObject/C_DestroyObject,
so those calls cost hundreds of sector erases and tens of seconds on
flash with slow erase times.
Cache modified sectors in RAM and commit them together when the store
window closes:
- sector cache sized to the worst-case span of one object plus the
header sector (WOLFBOOT_PKCS11_STORE_CACHE_SECTORS), LRU eviction
when exceeded
- header sector commits last, so a committed header is the atomic
commit point of the batch: power failure during a flush leaves the
flash in either the pre-batch or the post-batch state
- per-commit backup sector write preserved, keeping recovery of the
sector in flight at failure time
- delete_object commits on return (durability contract, unit-tested)
- nodes table, bitmap, payload ids and the live object size
(handle->size) are read from the cache when the sector is dirty
Measured on an STM32H5 with 8KB sectors, wolfPKCS11 in the secure
world: C_CreateObject 1.5s -> 0.15s, C_DestroyObject 1.3s -> 0.12s,
456 -> 40 sector erases per create, and the count no longer scales
with the number of objects in the token.
PKCS11_STORE_STATS (off by default) adds flash-activity counters and a
test-app bench to quantify store traffic: make PKCS11_STORE_STATS=1.
The cursors mem, mem_pf and io advanced as 32-bit values: after a
BAR allocation whose end is exactly the pool end 0x100000000 (now
reachable), *base = bar_value + length wrapped to 0. Every later
allocation then passed the start and end checks and programmed its
BAR at address 0, over the legacy IO range and DRAM.
Widen the three cursors to 64-bit and advance with a 64-bit sum, so
an exhausted pool leaves the cursor at the end. The address
parameters of pci_enum_next_aligned32 and pci_align_check_up widen
with them; pci_enum_next_aligned32 computes in uint64_t rather than
uintptr_t, which on 32-bit targets would truncate the exhausted
cursor back to 0 and defeat the addr > 0xffffffff rejection. The
programmed BAR value stays 32-bit.
The 4 GiB pool unit test now also adds a second device with a
preset (previously programmed) BAR: exactly filling the pool must
leave that BAR untouched instead of re-allocating it from a wrapped
cursor. The mock learns to seed a BAR preset from the bar info,
and the loop variable shadowing in test_pci_commit that it exposed
is fixed (the inner preset loop clobbered the outer node loop
counter, so only the first node was ever committed).
Verified: unit-pci 30/30, unit-pci-4gib green, full nxp_t1024
powerpc build green.
A pool ending exactly at 0x100000000 (e.g. 0xC0000000 + 0x40000000,
the classic top-half 32-bit MMIO layout) was a working configuration:
the old base + length - 1 initialization wrapped to 0xFFFFFFFF in
32-bit arithmetic. The overflow guard from the exclusive-limit fix
rejected such pools with base + length > 0xFFFFFFFF, aborting
enumeration - and the FSP caller discards the return value, so the
platform would boot with no PCI BARs programmed.
The limit fields cannot hold the exclusive end 0x100000000 while
32-bit, so widen mem_limit and mem_pf_limit (and the limit
parameters of pci_enum_next_aligned32 and pci_align_check_up, plus
the local in pci_program_bar) to 64-bit, and reject only pools whose
end is above the 32-bit space. The initialization now casts to
64-bit before the addition so the sum cannot wrap.
The T10xx PCIe setup initializes the same struct with the old
inclusive base + length - 1 form; align it to the exclusive
semantics the allocator enforces, or the last byte of the configured
pool is unusable.
New unit-pci-4gib build of the existing test file with the MMIO
pool [0xC0000000, 0x100000000): pci_enum_do() must accept the pool
and map a 1 MB BAR at the pool base. Fails on the old guard.
The WOLFBOOT_DTS_MAX_SIZE/WOLFBOOT_DTS_MIN_SIZE pair was defined
separately in update_disk.c (F-7066) and update_ram.c (pre-existing), so
the two copies could drift. Move it to include/fdt.h, the FDT dialect
header both translation units already pull in via image.h; the hal
override (nxp_ppc.h, included before fdt.h in boot_ppc.c) keeps its
precedence. Also replace the 'bounded by the staging region' comment,
which claimed more than the code guarantees: the copy is clamped to
WOLFBOOT_DTS_MAX_SIZE, so the staging window at
WOLFBOOT_LOAD_DTS_ADDRESS must be at least that large (or the bound
overridden for the target), and the header comment now says so.
Skoll review finding 3, 2026-08-21 wolfboot review.
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.
tfm.c never tests WC_NO_HARDEN, so dropping it enabled nothing; it only
un-silenced the advisory #warning in settings.h, which -Werror turns
into a build failure for any USE_FAST_MATH + WOLFCRYPT_TZ_PSA build.
Define TFM_TIMING_RESISTANT, which is what makes tfm.c constant time.
The comment justified WC_NO_HARDEN on the grounds that 'wolfBoot only
does public asymmetric operations'. That is not true for software
DICE: with WOLFCRYPT_TZ_PSA and no WOLFBOOT_DICE_HW, src/dice/dice.c
retrieves the private IAK (wolfboot_attest_get_private_key) and signs
the attestation claims with wc_CoseSign1_Sign_ex(), and arch.mk still
selects USE_FAST_MATH for those builds. A configuration maintainer
trusting the comment would have left the private-key signing path
without timing hardening.
Gate the definition to verify-only configurations: WC_NO_HARDEN is
now defined only when WOLFCRYPT_TZ_PSA is absent or WOLFBOOT_DICE_HW
is set (hardware DICE keeps signing in the crypto engine), and the
comment states the actual rationale.
Verified with a preprocessor probe: plain fast-math and hardware DICE
still get WC_NO_HARDEN; software DICE no longer does.
The comment labeled IMG_STATE_TESTING as an 'ELF loading state', but
the constant is wolfBoot's general dual-bank confirm/rollback marker:
it is set on the BOOT partition after every swap (update_flash.c,
update_flash_hwswap.c, update_ram.c - none ELF-specific) and cleared
by wolfBoot_success(); if still present at the next boot it triggers
a rollback. A maintainer trusting the old comment could have treated
the state as inert on non-ELF targets and disabled the anti-brick
path.
Reword to the accurate description already used in
docs/flash_partitions.md, keeping the note that it only lives on the
boot partition so it cannot conflict with IMAGE_STATE_UPDATING.
Comment-only change; no code or behavior changes.
- wolfBoot_fit_memcpy: return int so a failed PDMA copy propagates instead
of being swallowed. The weak default (memcpy) returns 0; the MPFS250 PDMA
override returns -1 if any chunk's mpfs_pdma_memcpy() fails. Callers now
fail closed: fit_load_image_inner returns NULL (kernel load then panics
via update_disk.c), the update_disk DTS copy panics, and hal_dts_fixup
returns an error on a failed L2->DDR copy-back.
- options.mk: gate -DSTACK_SIZE_PER_HART behind RISC-V arch (RISCV/RISCV64).
The macro is only consumed by the RISC-V startup asm and the mpfs250-m.ld
sed token, so it is no longer emitted for PPC/ARM/other targets. The
unconditional default (?= 0) is kept because the linker sed always needs
a value to substitute.