Commit Graph

1325 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 7896d2d70f F-14486: read policy_sign key files unbuffered
loadFile() in the TPM policy-signing tool read the private key
through a fully buffered stdio stream, leaving a libc-owned copy of
the key material that outlives the call. Open the file unbuffered
(setvbuf _IONBF), the same pattern sign.c already uses for its key
reads.
2026-09-30 16:33:53 +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
David Garske baf51e7078 tools: add an x86 FSP UPD decoder driven by the FspmUpd.h offset comments 2026-09-30 09:25:16 +02:00
Daniele Lacamera 770687185a raise footprint limits: NONE +24B, ECC256/ECC384 +12B each;
Re-measured all 22 stm32f407 configurations in the CI container
(ghcr.io/wolfssl/wolfboot-ci-arm:v1.0). Growth comes from the uart
printf %u/is_signed change, which is compiled into every build. RSA,
RSAPSS, LMS and ML_DSA measured 8B smaller than their limits; limits
left as-is (shrinks are not ratcheted down).
2026-09-29 10:22:07 +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 b896caeea0 F-14301, F-14302: keep no libc stdio copy of key material;
The keytools read and write private keys through fully buffered
stdio streams: for key files smaller than the stdio buffer, libc
copies the raw key bytes into its internal FILE buffer, and
fclose() frees that buffer without scrubbing it, so a copy of
every key survives the explicit zero_and_free()/wc_ForceZero()
wipes. Open every secret-material stream unbuffered with
setvbuf(f, NULL, _IONBF, 0) right after fopen():

- sign.c load_key(): the private key file. Also close the
  stream on the short-read failure path, which previously
  leaked it.
- sign.c make_header_ex(): the firmware encryption key/IV file.
- keygen.c: the five private-key write sites (RSA, ECC,
  Ed25519, Ed448, ML-DSA).
- lms_common.h / xmss_common.h: the LMS/XMSS private-state
  write, verify-read and read callbacks.

Public-key export streams are left buffered: not secret.
Host-side hardening only: whoever can read the tool heap can
read the key file too.
2026-09-29 09:13:25 +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
Daniele Lacamera 17e4327c51 footprint: SIGN=NONE limit 5172 -> 5180 2026-09-24 14:52:22 +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
Sameeh Jubran e237fa3cbf sbom: pin wolfGlass to 224a4f3 2026-09-24 13:10:18 +02:00
Sameeh Jubran 21ea4dc20a sbom: align comments with wolfcrypt PURL-only matching
Makefile no longer says a scan matches wolfssl:wolfcrypt. Re-vendor
wolfGlass at 48796ea so the generator contract exempts nested wolfcrypt.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 13:10:18 +02:00
Sameeh Jubran ec05d5a7d5 sbom: drop nested wolfcrypt CPE
Re-vendor wolfGlass at 3b708da. wolfcrypt stays a nested component
(name, version, PURL). Matching uses the wolfssl CPE only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 13:10:18 +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