Commit Graph

552 Commits (80dbb6da15ab5eb348f5d76049dabd03be5bdd67)

Author SHA1 Message Date
David Garske 80dbb6da15 update_disk: golden slot tried after the A/B attempts, exempt from anti-rollback and never written 2026-10-07 20:07:38 -07:00
David Garske 84603a5054 gzip: table-driven Huffman decoder by default, GZIP_SMALL=1 keeps the bit-serial one 2026-10-07 16:54:53 +02:00
David Garske 508ad6a26d Add wolfBoot target for Infineon PSOC Control C3 2026-10-06 18:14:10 +02:00
David Garske 97e895613d tpm: add a TIS-over-I2C transport and a generic I2C driver interface 2026-10-06 18:14:10 +02:00
Daniele Lacamera 6f6a1df84b prepare-release-v2.10.0: update submodules to latest releases
wolfSSL v5.9.4-stable, wolfPSA v5.9.4, wolfCOSE v2.0.0; wolfHSM
left at v1.5.0-8-g86dd6df. wolfPSA v5.9.4 changes the store Close
return to int; wolfCOSE v2.0.0 splits the source into 15 files and
drops the deprecated ES256 alg ID, so DICE now signs with ESP256.
2026-10-01 14:44:49 +02:00
Daniele Lacamera fd9158e217 multiboot: request-list entries are u32 per spec v2.0
The F-14470 fix changed the info-request entry width to 16-bit on a
misreading of the spec: Multiboot2 v2.0 defines mbi_tag_types as
u32[n] ("an array of u32's"), and the original code already divided
by sizeof(uint32_t). A 32-bit request {4} was misread as {4, 0}, so
the follow-up zero-skip was only papering over the misparse; real
FSP images with a mandatory request tag stopped booting (fsp_qemu
CI). Restore the u32 width, drop the zero-skip, and rebuild the
test fixtures with 32-bit entries.
2026-09-30 20:18:10 +02:00
Daniele Lacamera 48e06b898a multiboot: treat a zero request-list entry as padding
F-14470 corrected the request-list entry width to the spec's 16-bit,
which doubled the entry count. Intel FSP pads the list so the last
entry is zero (the end-tag type), and the old 32-bit width never
reached it. A zero entry is padding, not a mandatory request: skip
it, so FSP images keep booting. New test covers a zero-padded list.
2026-09-30 19:59:33 +02:00
Daniele Lacamera 2ac990b642 F-14485: snapshot the legacy PBKDF2 password length once
nsc_mech_prepare() read the non-secure legacy ulPasswordLen pointer
twice: once to size the secure password copy, once again (via nsc_in)
to fill the secure length word the library dereferences. A concurrent
non-secure write between the two reads could desync the length from
the password buffer size. Take a single snapshot and write it into
the secure length word directly. Add the missing test for the legacy
branch: the stub now consumes the length the way wolfPKCS11 does
(through the bounced pointer) and checks scrubbing.
2026-09-30 16:33:53 +02:00
Daniele Lacamera 4eb6305c54 F-14470: accept optional/missing multiboot2 info requests
mb2_build_boot_info_header() rejected a valid OS image header when
the optional information-request tag was missing, and rejected any
request type it did not implement without checking the tag's
optional flag (bit 0) the spec defines for skipping. A well-formed
header without the tag now requests nothing, optional unsupported
requests are skipped, and only a mandatory unsupported one rejects
the image. The finder returns NULL for a missing tag and for a
malformed tag list alike, so the absent case walks the list to tell
the two apart: any corrupt tag rejects the header. Tests updated to
the spec semantics (optionality driven by the tag flags) plus
malformed-list cases.
2026-09-30 16:33:53 +02:00
Daniele Lacamera 45e35ddb2d F-14469: quad-enable writes back the status register it read
qspi_quad_enable() memset the status register it had just read before
setting the QE bit, so the write-back zeroed every other writable bit
(SRL/CMP in SR2, BP/SRWD on the SR1 path) on the first probe of a part
with QE clear. Drop the memset so the write is a read-modify-write.
Test pins the preserved-bits write and the no-write sunny path.
2026-09-30 16:33:53 +02:00
Daniele Lacamera 7069f986ca F-14468: record verify-failure version via get_image_version
wolfBoot_record_verify_failure() read the image version from
img->hdr, which for an external-flash image is a device offset, not
a CPU address: the persistent record stored a garbage version, and
the read can fault where the offset is unmapped. Read the version
through wolfBoot_get_image_version(part), which fetches external
headers via ext_flash_check_read. No unit test compiles
update_flash.c with WOLFBOOT_PERSIST_FAILURE_STATUS, so add a
compile check for that ext-flash configuration to the gate.
2026-09-30 16:33:53 +02:00
Daniele Lacamera 7913b91c99 F-14484: reject non-BOOT/UPDATE parts in get_partition_state
wolfBoot_get_partition_state() only rejected PART_NONE, so any other
part byte (SWAP, SELF, unknown) passed through the NSC veneer to a
NULL dereference of get_partition_magic() in secure code. Reject
every part without a trailer at the shared entry; the NSC veneer
delegates here, so a non-secure caller can no longer crash the
secure world. The mock get_trailer_at() now matches the real one
(NULL for parts without a trailer), which is what lets the test
prove the deref.
2026-09-30 16:33:53 +02:00
Daniele Lacamera 3cd94aa3f0 F-14483: pin the RSA DigestInfo in RsaDecodeSignature
The parser read the SEQUENCE length without checking it, skipped the
AlgorithmIdentifier without comparing it, and ignored trailing bytes,
so only the digest length was pinned. Any free byte in the message is
forgeable with a low-exponent key (e=3) via Bleichenbacher 2006, and
keygen -i imports exponents unchecked. Require the SEQUENCE to span
the whole payload, the AlgorithmIdentifier to match the configured
hash OID byte for byte, and the OCTET STRING to end at the payload
end. Test pins the exact wc_EncodeSignature() encoding and rejects
trailing bytes and the empty-algoid forge shape.
2026-09-30 16:33:53 +02:00
David Garske 4c79eee999 x86 FSP: fix linux-loader unit-test link for wolfBoot extent symbols and guard the setup-header read 2026-09-30 09:25:16 +02:00
David Garske 8865adc01c x86 FSP: split kernel copy vs decompress overlap checks and gate on boot protocol 2.10 2026-09-30 09:25:16 +02:00
David Garske 3d197cb8c7 x86 FSP: load an optional initrd alongside the Linux bzImage payload 2026-09-30 09:25:16 +02:00
Daniele Lacamera a397d90aff F-14272: consume the DISABLE_BACKUP update by erasing the trailer;
Programming IMG_STATE_NEW (0xFF) over the update state is a no-op on
NOR flash: programming can only clear bits, so 0x70 (UPDATING) & 0xFF
leaves 0x70 and the update is re-attempted on every boot. Erase the
update partition's trailer sector(s) instead, exactly as the swap
path's final erase does - an erased trailer reads as NEW through the
state machine (the magic is gone, so the state read fails and
wolfBoot_start keeps its IMG_STATE_NEW initial state).

The unit test now asserts the trailer was erased (erased_update >= 1)
and that the state read fails after the update; it fails against the
old program-based fix.
2026-09-29 09:46:51 +02:00
Daniele Lacamera 798bfc26b2 F-14300: clear TPM auth slots and device object after use;
wolfBoot_unseal_blob, wolfBoot_read_blob and wolfBoot_check_rot
set the WOLFTPM2_DEV auth slot 0 with the blob/RoT auth value but
only cleared the policy session (slot 1), leaving the auth value
in SRAM after the call. Add wolfTPM2_UnsetAuth(&wolftpm_dev, 0)
on every exit path, matching the existing timestamp/quote
pattern in the same file.

wolfBoot_tpm2_deinit() scrubbed the session and SRK objects but
not the device object, which also holds the last auth slot
contents and the last command/response buffer. Add
TPM2_ForceZero(&wolftpm_dev, sizeof(wolftpm_dev)) on all build
paths, completing the 'leave no session key or SRK auth in SRAM'
intent the deinit comment already states.

Unit tests assert the slot is unset after unseal/read/check_rot
and that deinit scrubs the device object last.
2026-09-29 09:06:10 +02:00
Daniele Lacamera d4cf395dc2 F-14269: print %u as unsigned in the 32-bit printf path
The u/i/d conversions shared one branch that read the argument as int
and ran it through the signed decimal path of uart_writenum, so %u
values >= 2^31 printed as negative numbers (0xFFFFFFFF became -1).
Read %u as unsigned int and add an is_signed parameter to
uart_writenum so the sign handling applies only to d/i, matching the
is_negative design of the 64-bit helper.
2026-09-29 08:47:18 +02:00
Daniele Lacamera 8bdcc4e761 F-14272: consume the update state in the DISABLE_BACKUP path
The direct update->BOOT copy left the update partition at
IMG_STATE_UPDATING, so wolfBoot_start() re-entered wolfBoot_update(0)
on every boot: a wasted verification, and with ALLOW_DOWNGRADE a full
BOOT re-flash on every power-up. Reset the update state to NEW after
the update succeeds, as the swap path's final erase does; the write is
best effort, so in setups where the bootloader cannot write the update
partition the documented application requirement covers it. Add a
DISABLE_BACKUP unit variant with a state-consumption test.
2026-09-29 08:43:26 +02:00
Daniele Lacamera 669829cb3a F-14270: propagate flash failures in swap_and_final_erase
The final swap phase ignored the return values of the staging sector
backup, the boot trailer erase and the restore copy, so a failed copy
still marked BOOT as TESTING and erased the UPDATE trailer. Check each
operation and abort with the flash locked before advancing the state;
the existing resume machinery in wolfBoot_start then retries or rolls
back. Add failing-copy tests for the backup and restore paths.
2026-09-29 08:37:29 +02:00
David Garske 8f4578c84e Add optional boot confirmation and rollback for the disk boot path 2026-09-29 07:41:43 +02:00
David Garske df9367297a
Merge pull request #910 from danielinux/fenrir-fixes-2026-09-24
Batch of Fenrir fixes: 2026 09 24
2026-09-28 10:36:12 -07:00
David Garske 490a3791f9 Fix to keep the FIT DTB's own bootargs unless LINUX_BOOTARGS is explicitly configured 2026-09-24 16:03:12 +02:00
David Garske 23989067ec zynqmp: add non-cacheable DMA window and hal_dma_set_noncached() 2026-09-24 13:27:04 +02:00
Aidan Garske 62c7fe30b2 Add unit tests for the i.MX RT700 flash address gate 2026-09-24 13:12:36 +02:00
Daniele Lacamera 815c4616c4 F-11023: add the STM32G0 copy of the fast-path write test
The G0 and G4 files carry the same double-word fast path and were
fixed in the same commit, but only the G4 copy had a regression
test. Clone the G4 harness with the G0 register set (BSY1, 4-bit
W1C clear).
2026-09-24 12:03:28 +02:00
Daniele Lacamera 2f73f50f59 F-12885: reset the LCR counter in the no-fork x86 uart tests
The Check fixture runs once per case, so under CK_FORK=no the
rejection tests saw LCR writes from the earlier tests and failed.
2026-09-24 12:03:28 +02:00
Daniele Lacamera 66327766ca F-12884: drop the unused variable in the nrf54l write test 2026-09-24 12:03:28 +02:00
Daniele Lacamera 839a1d2d9e F-13609: return the truncated length for over-long tokens
Returning -1 for a truncated token contradicted the documented
contract (token length, 0 at end of list) and skipped the caller's
existing skip path for unmatchable names. Return the truncated
length: env_leftkey fails to match it and the slot is skipped.
2026-09-24 12:01:52 +02:00
Daniele Lacamera f528cabfd0 F-14131: pass the byte length to FLASH_EraseSector
The MCXA ROM API (fsl_romapi.h) takes lengthInBytes, not a sector
count, so the original pass-through was already correct and the
sector-count conversion erased almost nothing. It also called
FLASH_GetFlashConfig, which does not exist in the MCXA ROM API and
only compiled against the unit-test stub. Restore the byte length,
keep the len guard, and drop the invented call from the stub.
2026-09-24 12:01:43 +02:00
Daniele Lacamera 641c9cf4a0 F-12871: compare against the absolute bank 2 base
The dispatcher split cross-bank requests at FLASH_BANK2_BASE_REL
(0x00100000), but callers pass absolute flash addresses in
FLASHMEM_ADDRESS_SPACE, so the split never fired and the whole
request was programmed on the bank of the start address. Compare
against FLASH_BANK2_BASE (0x08100000); the test now drives
absolute addresses so it exercises the real call convention.
2026-09-24 12:01:43 +02:00
Daniele Lacamera 13448ab226 F-13609: consume over-long BOOT_ORDER tokens whole in env_next_name
A token longer than the name buffer left its remainder in the stream,
which the next call read as a second, spurious slot name. env_next_name
now consumes the whole token and returns -1; the select_slot gate
treats <= 0 as malformed. Adds unit tests driving the static directly
(70-char token with and without a trailing valid name).
2026-09-24 11:14:01 +02:00
Daniele Lacamera 93da3768d2 F-9742: reject cross-bank flash erase on TI Hercules
hal_flash_erase() selected the bank geometry from the start address
only; a range extending past the end of that bank was partially
erased and reported as success. Derive the bank end from the sector
geometry and reject cross-bank requests before erasing anything.

New unit test extracts the real hal_flash_erase() and runs it against
an emulated two-bank FAPI.
2026-09-24 10:52:19 +02:00
Daniele Lacamera d6464f96f9 hifive1: mask the HFROSCCFG divider shift with the divider field (F-9743)
HFROSCCFG_DIV_SHIFT(d) masked the shifted value with HFROSCCFG_TRIM
(bits 16-23) instead of HFROSCCFG_DIV (bits 0-3), so the divider
always programmed zero.

- mask with HFROSCCFG_DIV
- add unit-hifive1-hfrosccfg pinning the field macros (value,
  overlap, and the composed init register value)
2026-09-24 10:16:34 +02:00
Daniele Lacamera 87b89a0529 ramboot: accept image versions with the high bit set (F-13606)
wolfBoot_get_blob_version() returns uint32_t, but the value was
assigned to 'int ret' and rejected when 'ret <= 0'. Versions with
bit 31 set (e.g. 0x80000001) are valid unsigned versions and were
silently rejected with 'No valid image found'.

- check the version as uint32_t, rejecting only version 0
- add test_ramboot_high_bit_version (version 0x80000001 ramboots)
2026-09-24 10:16:28 +02:00
Daniele Lacamera e6824ae221 ramboot: require full payload size from ext_flash_read (F-13605)
- ext_flash_read() backends return the number of bytes read; the
  ramboot payload copy only rejected negative returns, so a positive
  short read left a truncated image in the RAM load region (executed
  when WOLFBOOT_SKIP_BOOT_VERIFY is set)
- require ret == img_size, mirroring the header read in the same
  function and the wolfBoot_start() copy (F-12065 fix)
- unit test: mock short read on the payload copy aborts ramboot
2026-09-24 09:17:54 +02:00
Daniele Lacamera 647022b183 x86: derive UART stop-bit field from requested stop count (F-12885)
uart_init() initialized a local to zero and tested that local, so the
LCR stop-bit field was always zero (one stop bit) and stop values
other than 1/2 were silently accepted. Validate stop against 1/2 and
derive the field from the parameter.

Add unit-x86-uart (linked real driver, io_write8/io_read8 mocked)
covering 8N1/8N2/5O2 LCR encodings plus stop-count and existing
argument rejection; 3 of 5 tests fail pre-fix.
2026-09-24 09:11:56 +02:00
Daniele Lacamera 0702a6123c F-11048: clamp negative '*' field widths in uart_vprintf
A negative '*' width passed the upper-bound-only clamp and reached
the zero-pad memset as a huge size_t (stack overflow). Clamp to zero
at the parser, the single source of dynamic widths; the existing
zero-to-8 default then applies.

New test test_uart_printf_negative_star_width covers %0*x and
%0*llu; SIGSEGV pre-fix, green post-fix. Full unit suite green.
2026-09-24 09:06:32 +02:00
Daniele Lacamera d6926a7c1f F-11030: consume long/size_t/pointer args at full width in uart_vprintf
The UART formatter recognized 'l' and 'z' length modifiers and %p,
but still consumed the argument as int, truncating long, size_t and
pointers on 64-bit targets (undefined behavior). Track the 'z'
modifier and, where PRINTF_LONG_LONG is enabled, consume long /
unsigned long / void * and format through the 64-bit
uart_writenum_ll() path; the 32-bit path is unchanged (long == int
width there).

New test test_uart_printf_64bit_args covers %ld, %lu, %zu, %p, %lx
with values past 32 bits plus %lld and %d regressions; fails
pre-fix (truncated output), green post-fix. 32-bit compile of
string.c verified with -m32 -Wall -Wextra.
2026-09-24 09:04:29 +02:00
Daniele Lacamera a2ad3c5c37 F-13607: bound the whole object header in the store guards
The update_store_size()/store_live_size() guards only bounded the
first byte of the 32-byte struct obj_hdr (and used '>'), so a header
offset in (SECTOR_SIZE-20, SECTOR_SIZE] would write/read the 'size'
field into the adjacent cache slot. Compute the offset first and
reject anything past SECTOR_SIZE - sizeof(struct obj_hdr), in all
three copies (pkcs11 update_store_size, pkcs11 store_live_size,
psa update_store_size).

New tests forge a header at SECTOR_SIZE-16 and assert the write is
rejected without touching the adjacent slot (pkcs11) / without any
flash traffic (psa). Full unit suite green.
2026-09-24 08:59:18 +02:00
Daniele Lacamera 9e9cdbef0f F-12070: validate the persisted slot position in find_object_buffer
A corrupted node-table pos was used to compute the object address
without a range check, unlike bitmap_put() which rejects positions
outside KEYVAULT_MAX_ITEMS. Both storage implementations now delete
the node and return NULL when pos is out of range, matching the
existing "cannot recover object payload" path.

Verification: new test seeds the node table with pos =
KEYVAULT_MAX_ITEMS and matching id's in the backup sector, so the
unfixed restore path reaches the out-of-range slot (mock flash
fails on the erase); fails pre-fix in unit-psa_store and
unit-pkcs11_store, 10/10 and 13/13 post-fix; full
make -C tools/unit-tests run green.
2026-09-24 08:51:11 +02:00
Daniele Lacamera 66b2205e9b F-12106: report flash programming errors on STM32U3
hal_flash_write() cleared errors before programming and waited for
completion but never read the post-program status register, so
OPERR/PROGERR/WRPERR/PGAERR/SIZERR/PGSERR faults were swallowed and a
failed program was reported as success. Check FLASH_NS_SR after the
wait, clear the flags, and return -1 on fault; reject addresses not
aligned to the 8-byte double-word programming unit.

Unit test extracts the writer with stubbed registers: success,
PROGERR, WRPERR and misaligned-address cases; fails pre-fix.
2026-09-24 07:04:58 +02:00
Daniele Lacamera 58061721ee F-12871: split cross-bank flash writes at the bank boundary
hal_flash_write() picked the bank from the start address and
programmed the whole request on that bank, so a request crossing
into bank 2 wrote its tail to the wrong bank. The exported writer
now splits at FLASH_BANK2_BASE_REL into two per-bank passes; a
failure in the first pass stops the split.

Unit test extracts the dispatcher and mocks the per-bank pass:
cross-boundary split, single-bank pass-through, and first-pass
failure all covered; fails pre-fix.
2026-09-24 07:00:28 +02:00
Daniele Lacamera 65e3b40361 F-12884: flush the nRF54L UART conversion buffer when full
Input longer than the 128-byte conversion buffer was silently
truncated: the loop stopped at the buffer edge and one raw write
dropped the rest. Flush on full (and before a newline that no longer
fits) and keep converting.

Verification: new unit-nrf54l-uart-write fails pre-fix 3/4 (output
capped at 128 bytes), 4/4 post-fix; full make -C tools/unit-tests run
green.
2026-09-24 06:47:51 +02:00
Daniele Lacamera 1d1977c241 F-13654: index the NVM word fast path from the current offset
With matched-but-unaligned address/data residues the byte-wise prefix
leaves i % 4 != 0, so dst[i >> 2] stored the word below the intended
address, dropped the tail bytes of the unit, and issued an unaligned
NVM store (HardFault on M0+). Derive src/dst from address + i, matching
the already-correct else branch.

Verification: new matched-alignment test fails pre-fix in both
unit-flash-write-samr21 and unit-flash-write-same51 (tail bytes stay
0xFF), 3/3 post-fix; full make -C tools/unit-tests run green.
2026-09-24 02:49:58 +02:00
Daniele Lacamera 6025f3540f F-13655: index the flash double-word fast path from the current offset
With an unaligned start address the byte-wise prefix leaves i % 4 != 0,
so dst[i >> 2] programmed the double word below the intended address
and left the tail bytes of the unit unprogrammed. Derive src/dst from
address + i, matching the already-fixed else branch.

Verification: new unit-stm32g4-write test_write_unaligned_start fails
pre-fix (memcmp mismatch), 5/5 post-fix; full make -C tools/unit-tests
run green.
2026-09-24 02:47:16 +02:00
Daniele Lacamera f0e2c49150 F-13656: bound e_phentsize to the program-header struct size
An undersized e_phentsize made the last program-header loop iteration
read past the validated table (and past the image buffer when the
table ends at the image end). Reject such images with -3.

Verification: tools/unit-tests/unit-elf-phentsize fails pre-fix
(undersized ELF accepted, ret=0), passes post-fix (2/2); full
make -C tools/unit-tests run green.
2026-09-24 02:44:03 +02:00
Daniele Lacamera d17ea7aefe F-14129: derive the C0 erase page from the flash-relative offset 2026-09-24 02:34:03 +02:00
Daniele Lacamera 5e065c67fe F-14130: guard zero-length erase in the kinetis HAL 2026-09-24 02:34:03 +02:00