The scattered-ELF flash integrity check's final image_CT_compare()
rejection branch had zero unit test coverage, so a weakened comparison
(e.g. != 0 -> == 0, or a dropped return -2) would silently pass. Add a
positive case (correctly-hashed scattered image verifies OK) and a
negative case (single corrupted byte in a scattered segment's
flash-resident payload is rejected via the digest-mismatch branch).
arg2num() parsed --custom-tlv values with signed strtoll(), which
saturates to LLONG_MAX (0x7FFFFFFFFFFFFFFF) on positive overflow. For
LEN==8 no masking is applied afterwards (unlike LEN 1/2/4), so any
value >= 2^63 silently encoded as 0x7FFFFFFFFFFFFFFF instead of the
value the user supplied, breaking the TLV encode/decode roundtrip.
Switch to strtoull() and reject (exit 16) when it reports ERANGE for
an 8-byte value, mirroring the existing fw_version range check.
update_disk.c wolfBoot_start() already zeroizes disk_encrypt_key/nonce and
the decrypted header before most wolfBoot_panic() halts, but four paths
were missed: anti-rollback rejection, FIT FPGA load failure, FIT kernel
load failure, and flash-protect failure. Since wolfBoot_panic() halts
forever on real targets, these paths left the key live in BSS on a halted
device. Add the same disk_decrypted_header_clear()/disk_crypto_clear()
pair used on the other panic paths immediately before each.
bmpatch mmaps `base` sized to the source file (len1) then memcpy's the
reconstructed output into base+len3, which runs past the mapping once the
destination image is larger than the source (a common case for delta
updates). This causes writes into the zero-fill slack of the last mapped
page to be silently dropped (never synced to the file even after the
trailing ftruncate) or, for larger overruns, a SIGBUS. Reproduced with the
existing `delta-test` fixtures: patching 1.txt back to the (larger) 0.txt
already corrupts the tail of the output today.
Write reconstructed blocks with pwrite(fd1, ...) instead of memcpy into the
undersized mapping; regular file writes grow the file naturally instead of
faulting/silently discarding data past the mapped region. The mmap of
`base` is only used for reads, so this is a minimal, in-place-preserving
fix. Verified `make -C tools/delta delta-test` now passes both directions
(shrinking 0->1 and growing 1->0).
hal_flash_write() and hal_flash_erase() aligned the invalidation start
address down to a 32-byte cache line but rounded the length up from
"len" alone, omitting the (address - aligned_address) offset. Whenever
(address % 32) + (len % 32) > 32, the invalidated range fell short of
address + len, leaving the last cache line stale after a write/erase.
Extract the range computation into hal_flash_cache_align_range()
(hal/imx_rt.h, dependency-free so it's unit-testable without the
NXP SDK) and include the down-alignment offset before rounding the
length up, so the invalidated range always covers [address, address+len).
FPGA_NONFATAL downgrades a failed PL bitstream load from fatal (panic)
to a logged non-fatal warning, letting boot continue without the
programmable logic. Every other fail-safe-weakening option in this
file (ALLOW_DOWNGRADE, DISABLE_BACKUP,
WOLFBOOT_UDS_UID_FALLBACK_FORTEST) emits a $(warning ...) so the
tradeoff is visible at build time; FPGA_NONFATAL was missing one.
test_hwswap_highversion_rollback_denied was the only test for
update_flash_hwswap.c's wolfBoot_start and always ends in boot_panic,
so the entire successful-boot path (IMG_STATE_UPDATING->TESTING
transitions before and after the HW dualbank swap, and the same-version
boundary of the anti-rollback check) was never exercised and mutations
there would go undetected. Add three tests that reach do_boot() and pin
each of those code paths.
otp-keystore-gen.c reads the device root UDS into the stack buffer
`uds` and copies it into the heap buffer `otp_buf` at OTP_UDS_OFFSET,
then on every exit path calls free(otp_buf) without wiping it first,
and never clears `uds`. Both copies of the highest-value device secret
remain in the host process's freed heap chunk and stack frame.
Add a local secure_zero() helper (no wolfSSL dependency, matching this
standalone host tool's existing bare-gcc build) and call it on the
success path and on the write-failure/short-UDS-read error paths,
before free()/exit(), mirroring the zeroize-before-release pattern
already used elsewhere in this tree (src/x86/ata.c,
src/x86/ahci.c). Paths that exit before `uds` is populated are left
untouched since there is no secret to wipe yet.
In the unaligned/partial-word path, address/len were advanced by the full
flash-word-relative loop index "i" (which starts at start_off), instead of
by the number of data bytes actually consumed (i - start_off). On a write
spanning more than one flash word this drops start_off bytes of input data
and misdirects the following word write. Mirrors the already-correct form
in hal/kinetis_kl26.c.
A matched block's source offset is encoded as off[0..2] right after the
ESC (0x7f) header marker. When the match offset falls in
[0x7f0000, 0x7fffff], off[0] is 0x7f, so the header begins with ESC ESC
-- the same two bytes wb_patch uses to decode an escaped literal 0x7f.
The decoder then emits a single literal byte, consumes only 2 of the 6
header bytes, and desyncs the rest of the stream, breaking the
wb_patch(wb_diff(A,B)) == B roundtrip for base images >= ~8MB (a
supported MMU/Linux delta-update configuration).
Make wb_diff skip any candidate match whose offset's most-significant
byte equals ESC, in both the forward (base-image) and backward
(previously-patched-image) search paths, so the ambiguous header is
never produced; the position falls back to literal encoding instead.
security_command_passphrase() copies the plaintext disk-unlock secret
into the file-static DMA buffer `buffer` at ATA_SECURITY_PASSWORD_OFFSET
but never wipes it. On the slot<0 early return no ATA command is even
dispatched, and on failures of ata_security_set_password()/
ata_security_unlock_device() (which sata_unlock_disk() turns into a
panic()) no later IDENTIFY DMA ever overwrites it, so the secret is
left resident in BSS for as long as the device stays powered.
Zeroize the password field on the slot<0 path and after synchronous
command completion. The async path (used only by the currently
unreachable ata_security_erase_unit()) is left untouched because the
HBA may still be DMAing out of the buffer when exec_cmd_slot_ex()
returns ATA_ERR_BUSY; wiping it there would race the transfer.
sata_unlock_disk() calls panic() directly when the post-unlock ATA
security state doesn't match the expected SEC5/SEC6, bypassing the
cleanup: label that zeroizes the plaintext secret[] stack buffer. On
x86, panic() is an infinite hlt() loop, so the plaintext disk-unlock
secret (TPM-unsealed key or password) remains readable in DRAM for as
long as the device stays halted. Zeroize secret[] before panic() on
this path, mirroring the existing cleanup zeroization.
Add tests asserting rejection on a mismatched pubkey hint and on a
short digestSz from wolfTPM2_NVReadAuth, so a weakened comparison
(e.g. == 0 -> >= 0, or a dropped clause) is caught.
cache_commit() is the single choke point through which every psa_store
helper (create_object, delete_object, wolfPSA_Store_Write's sector loop,
update_store_size, erase_object_payload, check_vault) flushes the static
cached_sector staging buffer to flash. The buffer was never cleared, so
key-object plaintext staged there could remain resident in static SRAM
after the store operation returned. Wipe cached_sector with wc_ForceZero
right after the flash write completes, mirroring the existing pattern in
src/wolfhsm_flash_hal.c.
The sbom target fed `cc -dM -E` an empty /dev/null with only
WOLFSSL_USER_SETTINGS, so gen-sbom's build properties collapsed to host
compiler builtins (all filtered out as noise) and the SBOM did not reflect
the selected configuration. Harvest the actual -D config macros from CFLAGS
(filtered in the shell so defines whose values contain spaces, e.g.
BOOTLOADER_PARTITION_SIZE=$((...)), survive intact) so the SBOM captures the
real TARGET/SIGN/HASH/feature set.
Also:
- add preflight checks for empty WOLFBOOT_VERSION and missing GEN_SBOM
- pass sources via --srcs-file to avoid ARG_MAX on large configs
- include .S sources in the source set, not just .c
- make HOSTCC and GEN_SBOM overridable
- README: soften "CRA Compliance" wording, use wolfboot-<version>.* names,
fix dangling wolfssl/doc/CRA.md link to point at the wolfSSL CRA Kit
Signed-off-by: Sameeh Jubran <sameeh.j@gmail.com>
Adds sbom target that produces CycloneDX and SPDX
SBOM files for a specific TARGET+SIGN combination.
Sources extracted from OBJS variable via wildcard.
wolfcrypt sources compiled into wolfBoot are listed
as wolfBoot's own sources (not a separate component).
Requires GEN_SBOM or WOLFBOOT_LIB_WOLFSSL set.