F-6878: delta: reject non-multiple SECTOR_SIZE/DELTA_BLOCK_SIZE at build

The per-sector fill loop in wolfBoot_delta_update() advances in
DELTA_BLOCK_SIZE steps, so a WOLFBOOT_SECTOR_SIZE that is not a multiple
of DELTA_BLOCK_SIZE writes past the one-sector SWAP partition and
misaligns the resume path. Enforce the invariant with a #error and add a
negative build test (unit-delta-sector-align.py).

Verification: full unit suite 1096 checks, 0 failures; new test fails
pre-fix (misaligned config built), passes post-fix (build rejected).
pull/882/head
Daniele Lacamera 2026-09-04 09:46:27 +02:00
parent 6883532da8
commit f598b3fd41
3 changed files with 84 additions and 0 deletions

View File

@ -623,6 +623,13 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume)
# define DELTA_BLOCK_SIZE 1024
#endif
/* The per-sector fill loop advances in DELTA_BLOCK_SIZE steps, so a
* sector that is not a multiple of the block size would be written
* past the one-sector SWAP partition and misalign the resume path. */
#if (WOLFBOOT_SECTOR_SIZE % DELTA_BLOCK_SIZE) != 0
#error "Delta update: WOLFBOOT_SECTOR_SIZE % DELTA_BLOCK_SIZE != 0"
#endif
static inline uint32_t wb_delta_im2n(uint32_t val)
{
#ifdef BIG_ENDIAN_ORDER

View File

@ -221,6 +221,7 @@ run: $(TESTS)
python3 unit-sign-delta-tlv.py || exit 1
python3 unit-sign-delta-cert-inv-off.py || exit 1
python3 unit-sign-delta-basehash-cleanup.py || exit 1
python3 unit-delta-sector-align.py || exit 1
python3 unit-sign-custom-tlv-le.py || exit 1
python3 unit-sign-custom-tlv-large.py || exit 1
python3 unit-sign-custom-tlv-pubkey-der.py || exit 1
@ -264,6 +265,14 @@ unit-update-flash-delta:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \
-DDELTA_UPDATES -DDELTA_BLOCK_SIZE=512 -D__WOLFBOOT \
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
# Intentionally misaligned config: the mock WOLFBOOT_SECTOR_SIZE (0x400) is
# not a multiple of DELTA_BLOCK_SIZE (1536). Must fail to build with the
# delta alignment #error. Used by unit-delta-sector-align.py only, so it is
# not part of $(TESTS).
unit-update-flash-delta-misalign:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \
-DDELTA_UPDATES -DDELTA_BLOCK_SIZE=1536 -D__WOLFBOOT \
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
unit-update-flash-self-update:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \
-DRAM_CODE -DARCH_SIM -DUNIT_TEST_SELF_UPDATE_ONLY \
@ -808,6 +817,10 @@ unit-update-flash-delta: ../../include/target.h unit-update-flash.c
gcc -o $@ unit-update-flash.c ../../src/image.c ../../src/delta.c \
$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
unit-update-flash-delta-misalign: ../../include/target.h unit-update-flash.c
gcc -o $@ unit-update-flash.c ../../src/image.c ../../src/delta.c \
$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS)
unit-update-flash-enc:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT \
-DPART_SWAP_EXT -DEXT_ENCRYPTED -DENCRYPT_WITH_CHACHA -DHAVE_CHACHA \

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
# unit-delta-sector-align.py
#
# Build-time invariant for DELTA_UPDATES: the per-sector fill loop in
# wolfBoot_delta_update() (src/update_flash.c) calls wb_patch() with
# DELTA_BLOCK_SIZE chunks until WOLFBOOT_SECTOR_SIZE bytes are produced, so
# the reconstructed image is only sector-aligned when WOLFBOOT_SECTOR_SIZE is
# a multiple of DELTA_BLOCK_SIZE. A mismatched config used to compile fine
# and corrupt the delta apply (write past the one-sector SWAP partition,
# misaligned resume); the build now rejects it with an #error.
#
# This test builds the real update_flash.c (via unit-update-flash.c) with a
# deliberately misaligned DELTA_BLOCK_SIZE and asserts the build fails with
# the invariant message.
#
# Copyright (C) 2026 wolfSSL Inc.
#
# This file is part of wolfBoot.
#
# wolfBoot is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# wolfBoot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
import os
import subprocess
import sys
MESSAGE = "WOLFBOOT_SECTOR_SIZE % DELTA_BLOCK_SIZE != 0"
def main():
# The misalign target is not in $(TESTS) and its rule does not track
# src/update_flash.c (included via unit-update-flash.c), so drop any
# stale artifact to force a real rebuild.
try:
os.remove("unit-update-flash-delta-misalign")
except FileNotFoundError:
pass
p = subprocess.run(["make", "unit-update-flash-delta-misalign"],
capture_output=True, text=True)
if p.returncode == 0:
print("FAIL: misaligned DELTA_BLOCK_SIZE config built, "
"expected the sector alignment #error")
return 1
if MESSAGE not in (p.stdout + p.stderr):
print("FAIL: build failed for the wrong reason:\n")
print(p.stderr[-2000:])
return 1
print("PASS: misaligned DELTA_BLOCK_SIZE rejected at build time")
return 0
if __name__ == "__main__":
sys.exit(main())