Commit Graph

79 Commits (740383a3f53b932dca2ed1ae3e97274805bd0573)

Author SHA1 Message Date
Daniele Lacamera 740383a3f5 F-4790: clamp OTP pubkey_size in keystore_get_size to prevent OOB read
keystore_get_size() returned slot->pubkey_size verbatim from the OTP
keystore slot with no upper bound. A corrupted or mis-provisioned slot
with pubkey_size > KEYSTORE_PUBKEY_SIZE produces a positive value that
passes every caller guard (pubkey_sz < 0 / <= 0). The callers in image.c
(key_sha256/key_sha384/key_sha3_384 and the ECC verify y-coordinate
offset) then read past otp_slot_item_cache, which only holds
KEYSTORE_PUBKEY_SIZE pubkey bytes.

Reject an out-of-range pubkey_size by returning -1, matching the existing
defensive validation of item_count in keystore_num_pubkeys() and the -1
error convention the callers already handle.

Add unit-otp-keystore, which compiles flash_otp_keystore.c in isolation
and verifies keystore_get_size() rejects oversized slots.
2026-06-09 15:52:07 +02:00
Daniele Lacamera 03f5193dc2 F-5129: fix stm32h7 hal_flash_erase Bank 2 sector underflow
The Bank 2 branch of hal_flash_erase() subtracted the absolute base
FLASH_BANK2_BASE (0x08100000) from the bank-relative loop offset p,
instead of the relative FLASH_BANK2_BASE_REL (0x00100000) used by every
other comparison in the function. For a Bank 2 offset (p >= 0x00100000)
this underflowed uint32_t to ~0xF8000000, with two effects:

  1. the SNB sector index programmed into FLASH_CR2 came from the
     underflowed value (always sector 0, with stray high bits leaking
     into other CR2 fields), and
  2. the subtraction mutated the loop variable p itself, so after
     p += FLASH_PAGE_SIZE the offset jumped past any end_address and the
     loop exited after a single iteration.

The combined result: any multi-sector Bank 2 erase touched only one
sector with the wrong index, silently leaving the remaining requested
sectors (e.g. the SWAP partition at 0x081C0000) unerased.

Compute the sector index in a temporary from FLASH_BANK2_BASE_REL so the
loop variable is preserved, and mask it with FLASH_CR_SNB_MASK before
shifting into FLASH_CR2.

Adds unit-flash-erase-h7, which compiles hal_flash_erase() in isolation
(guarded by WOLFBOOT_UNIT_TEST_FLASH_ERASE, mirroring the unit-mpusize
approach for boot_arm.c) against mocked flash registers and asserts that
a two-sector Bank 2 erase programs sectors 6 and 7 across two iterations.
2026-06-09 15:52:07 +02:00
Daniele Lacamera 74b8fc0664 F-5131: fix stale delta inverse-patch offset when cert chain expands header
base_diff() captured patch_inv_off = len3 + CMD.header_sz before calling
make_header_delta(), which signs the delta image via make_header_ex(is_diff=1).
When a certificate chain is present, the delta (is_diff=1) header needs ~72
more bytes than the non-delta header for the four delta TLVs plus the base-hash
TLV. For a window of cert-chain sizes, header_required_size(is_diff=0) still fit
the current CMD.header_sz while header_required_size(is_diff=1) did not, so
make_header_ex(is_diff=1) grew CMD.header_sz to the next power of two *after*
patch_inv_off was captured. The HDR_IMG_DELTA_INVERSE TLV then encoded a stale,
too-small offset; the bootloader (update_flash.c) uses it as a raw byte offset
into the update partition to locate the inverse patch, so rollback read from the
wrong offset and failed.

Resolve the is_diff=1 header-size expansion (same logic as make_header_ex)
before computing patch_inv_off. Add unit-sign-delta-cert-inv-off.py, which signs
an ed25519 delta with a 300-byte chain (inside the triggering window) and
asserts the inverse patch is the trailing HDR_IMG_DELTA_INVERSE_SIZE bytes of
the file; it fails before this fix.
2026-06-09 15:52:07 +02:00
Daniele Lacamera 8d235cf215 F-5132: complete mpusize() table so MPU stays enabled for >64KB wolfBoot
The mpusize() lookup in boot_arm.c only covered sizes up to 64KB and
returned MPUSIZE_ERR for anything larger. mpu_init() passes the wolfBoot
.text+.rodata span (_stored_data - _start_text) to mpusize() and bails
out at the MPUSIZE_ERR guard before reaching mpu_on(). Any build whose
bootloader image exceeds 64KB (TrustZone, PQC, delta-update, or several
crypto algorithms) therefore left MPU_CTRL clear, silently disabling all
five MPU regions for the lifetime of the bootloader.

Fill in the missing power-of-two entries from 128KB through 128MB
(ARMv7-M SIZE field = log2(bytes)-1, shifted into the RASR layout) so
the flash region size is resolved and mpu_on() is reached.

Add tools/unit-tests/unit-mpusize.c, which includes the real mpusize()
from boot_arm.c (guarded to its host-portable MPU helpers via
WOLFBOOT_UNIT_TEST_MPU) and checks that sizes above 64KB no longer map
to MPUSIZE_ERR. The test fails before this fix and passes after.
2026-06-09 15:52:07 +02:00
Daniele Lacamera 84d5bd3dde F-5352: emit 4-byte delta size TLVs from Python signer
sign.py encoded HDR_IMG_DELTA_SIZE and HDR_IMG_DELTA_INVERSE_SIZE with a
2-byte length via struct.pack("<H", ...), but wolfBoot_get_delta_info()
accepts those tags only when wolfBoot_find_header() returns
sizeof(uint32_t). Delta images produced by sign.py were therefore signed
with parseable TLVs yet rejected by the bootloader before the patch was
applied. Encode both size TLVs as 4-byte little-endian values, matching
sign.c (header_append_tag_u32) and the bootloader parser.

Add a regression test that signs a real delta image with sign.py and
asserts the bootloader-side parse recovers each delta TLV with the
required 4-byte length.
2026-06-09 15:50:48 +02:00
David Garske 14f6e4a298 Add wolfBoot FIT support for loading bitstream 2026-06-09 15:30:27 +02:00
Daniele Lacamera 3a8404b5e2 Fix test regression, addressed copilot comments 2026-06-05 20:55:52 +02:00
Daniele Lacamera d175c819cd F-4645: bound load_linux kernel size to prevent syssize*16 overflow
load_linux() computed the protected-mode kernel size as the uint32_t
product param.hdr.syssize * 16 (src/x86/linux_loader.c), where syssize
is copied verbatim from the (authenticated) bzImage at offset 0x1f4.
The multiplication wraps for any syssize > 0x0FFFFFFF: syssize=0x10000000
yields kernel_size=0 (DoS), and syssize=0x1FFFFFFF/0xFFFFFFFF yields
kernel_size=0xFFFFFFF0 (~4 GiB). That value fed straight into
memcpy((uint8_t*)KERNEL_LOAD_ADDRESS, linux_image + param_size,
kernel_size) with no cap, overwriting wolfBoot stage2, FSP data, and the
heap (CWE-190 -> CWE-680).

Fix at the root: linux_kernel_size() computes syssize * 16 in 64-bit and
rejects the image (panic) when the result is zero or does not fit in the
destination window [KERNEL_LOAD_ADDRESS, tolum). tolum is the top of low
usable memory the FSP already reports and that the ELF boot path uses as
its load upper bound (src/boot_x86_fsp_payload.c). The kernel load only
runs under WOLFBOOT_FSP (the non-FSP path panics earlier at the memory
map step), so tolum is always available there.

Add unit-linux-loader-syssize regression test (x86 32bit, standalone)
that feeds the PoC overflow values and asserts they are rejected while a
legitimate kernel and the exact-fit boundary are accepted.
2026-06-05 19:33:51 +02:00
Daniele Lacamera a8a9eec96b F-4711: bound e820 entries to prevent boot_params stack overflow
e820_add_entry_cb() appended every FSP-supplied resource descriptor into
boot_params->e820_table[] with no check against E820_MAX_ENTRIES_ZEROPAGE
(128). A HOB list with more than 128 EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
entries therefore wrote FSP-controlled addr/size/type triples past the
fixed-size table into the stack-allocated boot_params in load_linux(),
corrupting adjacent fields and the saved return address.

Reject any entry once the table is full (return non-zero, which aborts the
HOB iteration). e820_entries stays uint8_t since it is a fixed-offset field
in the Linux zero-page layout and the cap makes the 256 wrap unreachable.

Add unit-linux-loader-e820 regression test (x86 32bit, standalone) that
feeds 200 descriptors and asserts the table never overflows.
2026-06-05 19:19:05 +02:00
David Garske 9ca1d435b9 Peer review fixes (copilot)
src/fdt.c, include/fdt.h
  - Propagate fdt_fixup_initrd error in fit_load_ramdisk so a /chosen
    patch failure no longer silently boots a kernel with no initrd.
  - Add fit_load_image_to(): decompress (or memcpy) directly to a
    caller-supplied destination buffer instead of going through the
    FIT-declared `load` address. fit_load_ramdisk now uses this when
    WOLFBOOT_LOAD_RAMDISK_ADDRESS is set, so the override is a real
    safety bound for compressed ramdisks (previously the gzip stream
    was still inflated to the FIT `load` and only memcpy'd afterward).
  - Refactor fit_load_image_ex into a shared inner helper.
  - Reword the WOLFBOOT_FIT_MAX_DECOMP comment: the cap is a sanity
    ceiling, not a per-destination memory-safety bound. Authenticity
    is provided by the outer wolfBoot signature; tighter bounds need
    fit_load_image_ex / _to with an explicit out_max / dst_max.
  - Add WOLFBOOT_FIT_MAX_RAMDISK (defaults to WOLFBOOT_FIT_MAX_DECOMP)
    so targets can pin a tighter ramdisk decompression bound.

src/update_ram.c, src/update_disk.c
  - Panic when fit_load_image() returns NULL for the kernel subimage
    instead of letting load_address=NULL propagate into do_boot().

tools/unit-tests/unit-gzip.c
  - Add deterministic stored / fixed-Huffman / dynamic-Huffman gzip
    fixtures so the inflater's BTYPE 00/01/10 paths are exercised
    independent of host gzip(1) heuristics.
  - Add FEXTRA / FNAME / FCOMMENT / FHCRC and combined-flag fixtures
    plus a truncated-FEXTRA negative case to cover the optional gzip
    header parser.

tools/unit-tests/unit-fit-gzip.c (new), tools/unit-tests/Makefile
  - New libcheck binary covering the FIT loader's compression
    branches: gzip success, gzip stream corruption, unknown
    compression, compression="none" baseline, and the no-load
    fail-closed path. Built twice from the same source - once with
    WOLFBOOT_GZIP for the success / runtime-failure paths, and once
    without it so the compile-time fail-closed branch is also tested.
2026-05-05 10:16:16 +02:00
David Garske 090f0ef411 gzip: add clean-room RFC 1951/1952 inflater + libcheck tests
New src/gzip.c implements DEFLATE (RFC 1951) plus the gzip wrapper
(RFC 1952) from the RFC text only. Single-pass inflate, no allocations:
the output buffer doubles as the LZ77 sliding window, so back-references
read from out[out_pos - distance]. Canonical Huffman decode using
counts[] / symbols[] tables, ~10x smaller code than fast lookup tables
which matters in the bootloader. CRC32 + ISIZE verified against the
gzip trailer. Gated by WOLFBOOT_GZIP.

include/gzip.h carries the public entry point plus the RFC-canonical
constants (magic bytes, CM=DEFLATE, fixed Huffman boundaries, EOB
symbol, dynamic block field widths, run-length repeat metadata, CRC32
init/final-XOR, header/trailer sizes, alphabet sizes) so future
maintainers can cross-reference the RFC sections by name instead of
chasing literal numbers.

Tests in tools/unit-tests/unit-gzip.c round-trip 6 corpora through host
gzip(1) and back through wolfBoot_gunzip (empty, short text, all-zeros,
structured text, pseudo-random, ~2 MB kernel-sized). 9 negative cases
cover bad magic, bad CM, reserved FLG bits, truncated header,
truncated DEFLATE body, CRC32 mismatch, ISIZE mismatch, output overflow,
and NULL parameters. All 15 pass under libcheck.
2026-05-05 10:16:16 +02:00
Daniele Lacamera a60461f18b Fix XMSS keygen param fallback
F/3306
2026-04-29 12:30:23 +02:00
Daniele Lacamera cc6f52edb3 Add final sanity check after boot hook
F/3302
2026-04-29 12:20:41 +02:00
Daniele Lacamera 54b2ba34b7 Bound TPM name fallback copies
F/3300
2026-04-29 12:16:03 +02:00
Daniele Lacamera eaa6af2b66 Return UART flash read error on timeout
F/3039
2026-04-29 11:20:57 +02:00
Daniele Lacamera cffa75d4a4 Proper interface renaming + documentation 2026-04-22 14:14:28 +02:00
Daniele Lacamera b26d38ed41 Fix fTPM NSC bounds handling 2026-04-22 12:22:43 +02:00
Daniele Lacamera 9ac6b1f485 Fixed more regressions 2026-04-14 16:47:11 +02:00
Daniele Lacamera faebb62115 Fixed test regressions 2026-04-14 16:24:53 +02:00
Daniele Lacamera 4c0f425a7a Fix WOLFBOOT_MAX_SPACE precedence and test
F/2587
2026-04-14 14:28:44 +02:00
Daniele Lacamera 9265fe7984 loader: panic on TPM init failure
F/2569
2026-04-14 07:48:59 +02:00
Daniele Lacamera e10d9c1b19 Fix RAM fallback partition reselection
F/2567
2026-04-14 07:44:29 +02:00
Daniele Lacamera 00892c2644 Clamp unaligned SDHCI disk I/O spans
F/2566
2026-04-14 07:38:41 +02:00
Daniele Lacamera 8cdb8e9620 unit-tests: link common compare helper
Add src/string.c to TPM unit targets that compile tpm.c inline so wolfBoot_constant_compare resolves after its move out of TPM-only code.

F/CI
2026-04-10 06:08:47 +02:00
Daniele Lacamera 040bcdb00d unit-tests: skip large memmove under coverage 2026-04-09 06:23:40 +02:00
Daniele Lacamera c51bb6a7f5 unit-tests: exclude keytools from coverage 2026-04-08 21:54:30 +02:00
Daniele Lacamera 399e87508a unit-tests: fix update_ram protect context
Pass WOLFBOOT_ORIGIN and BOOTLOADER_PARTITION_SIZE to unit-update-ram and provide a hal_flash_protect stub so the update_ram harness still builds after bootloader protection was added.
2026-04-08 21:50:39 +02:00
Daniele Lacamera 0ab76ea4af tools/unit-tests: fix rebased sign test target
Remove leftover conflict markers after dropping 98d68c90 so unit-sign-encrypted-output builds again.
2026-04-08 19:54:57 +02:00
Daniele Lacamera e4e96ad2fa Reject valid zero-size delta images
F/2268
2026-04-08 19:53:50 +02:00
Daniele Lacamera 0c4be70a29 Add equal-version update-disk regression test
F/2264
2026-04-08 19:53:50 +02:00
Daniele Lacamera 47b61ba2eb Add RAM_CODE self-update unit coverage
F/2261
2026-04-08 19:53:50 +02:00
Daniele Lacamera c6e7f7966b Fix sign encrypted output open failure
F/1895
2026-04-08 19:53:50 +02:00
Daniele Lacamera 364b9d099b Fix policy_create PCR digest validation
F/1894
2026-04-08 19:51:17 +02:00
Daniele Lacamera 909fbeb35e (review comment) remove hal_otp.h 2026-03-31 07:20:45 +02:00
Daniele Lacamera 43b485f239 Validate FDT string offsets
F/1481
2026-03-30 16:23:16 +02:00
Daniele Lacamera 2abf80addd Guard TPM keystore auth in check_rot
F/1482
2026-03-30 16:23:16 +02:00
Daniele Lacamera 0039c8d980 Reject oversized TPM ROT auth input
F/1480
2026-03-30 16:23:16 +02:00
Daniele Lacamera ae7d23bf41 Fix stm32h5 OTP readonly block rounding
F/1475
2026-03-30 16:23:16 +02:00
Daniele Lacamera 5d0b224e29 Fix SDHCI response bit span check
F/1471
2026-03-30 16:23:16 +02:00
Daniele Lacamera 9b67b92609 Fix regression in unit tests 2026-03-23 08:04:48 +01:00
Daniele Lacamera 606e875670 Addressed copilot comment 2026-03-22 10:38:03 +01:00
Daniele Lacamera 20c3b86678 fix policy_sign digest parse checks
F/1100
2026-03-22 10:03:00 +01:00
Daniele Lacamera 5f919c1b2e Updated submodules 2026-03-20 14:57:43 +01:00
Daniele Lacamera 73b5ad46c9 fix store sbrk heap bounds handling
F/725
2026-03-16 09:32:51 +01:00
Daniele Lacamera ae1e9dfc06 Force-zero secrets in update_disk.c
F/97
2026-03-12 09:28:51 +01:00
Daniele Lacamera 397b896c42 key_sha384: zero hash buffer to cover for early error
F/370
2026-03-12 09:28:51 +01:00
Daniele Lacamera cea7f5d029 Check fw_size when WOLFBOOT_FIXED_PARTITIONS is off
F/373
2026-03-12 09:28:51 +01:00
Daniele Lacamera ec3e96995b Validate blob size from TPM NV storage
F/372
2026-03-12 09:28:51 +01:00
Daniele Lacamera 193d3d44c7 Fix more build issues in CI 2026-03-09 19:01:25 +01:00
Daniele Lacamera 9afe8a5e9a Do not rely on alignment when loading RSA exponent
F/369
2026-03-09 11:49:14 +01:00