Skip to content

boot: bootutil: fix out of bounds sector reads in swap using scratch - #2854

Open
ThePo1es wants to merge 1 commit into
mcu-tools:mainfrom
ThePo1es:fix-swap-scratch-slots-compatible
Open

boot: bootutil: fix out of bounds sector reads in swap using scratch#2854
ThePo1es wants to merge 1 commit into
mcu-tools:mainfrom
ThePo1es:fix-swap-scratch-slots-compatible

Conversation

@ThePo1es

@ThePo1es ThePo1es commented Sep 6, 2026

Copy link
Copy Markdown

Fixes #2697

boot_slots_compatible() and app_max_size() in swap_scratch.c index the sector table of a slot without checking that the slot still has sectors left, so with slots of a different number of sectors the loop reads past what boot_initialize_area() wrote. Changing the loop condition to && (as first suggested in the issue) breaks layouts where one sector of a slot covers several sectors of the other, e.g. the stm32f4SpiFlash sim device, so instead the slot sizes are compared with flash_area_get_size() first and the loop stops when the slot it is about to read has run out. The DECOMPRESS_IMAGES check for a zero-sized sector is dropped, it relied on the unwritten entries being zero, which is not the case in the sim (context_boot_go() keeps the buffer on the stack).

In a DECOMPRESS_IMAGES build a secondary slot larger than the primary, which the old code let through by way of those stale reads, is still accepted; the check is not made stricter there than it was. There is no decompress feature in the sim, so that path was only traced by hand.

The sim gets an unequal_slots_rejected test: the Nrf52840UnequalSlots* devices get a scratch area so a scratch build can boot them, and the test checks that the upgrade is refused and the flash unchanged. The test also passes on the current code, the stale reads happen to end in the same "not compatible" verdict, it pins down the behaviour now that it no longer depends on what the unwritten entries contain.

This does not conflict with #2792, which deprecates swap using scratch; nothing changes for equal-sized slots.

cargo test in sim/: default, overwrite-only, swap-move all pass.

boot_slots_compatible() and app_max_size() match the sectors of the
primary slot against the sectors of the secondary slot. The loop runs
while either slot has sectors left, but indexes the sector table of a
slot without checking that this slot has any left. With a different
number of sectors in the two slots it reads past the entries filled in
by boot_initialize_area(): zeros from the static buffer on a target,
whatever is on the stack in the simulator. With
MCUBOOT_DECOMPRESS_IMAGES that can make the slots pass as compatible.

Compare the slot sizes first, where they have to be equal, and stop
the loop when the slot about to be read has no sectors left. This also
replaces the DECOMPRESS_IMAGES check for a zero-sized sector, which
relied on the unwritten entries being zero.

Add a sim test booting a device with slots of different sizes in a
swap using scratch build; the upgrade has to be refused and the flash
left alone. The test also passes on the old code, where the stale
reads happen to end in the same verdict, it pins down the behaviour
now that it no longer depends on them.

Fixes mcu-tools#2697

Assisted-by: Claude:fable-5
Signed-off-by: ThePo1es <choijinbeom9@gmail.com>
@ThePo1es
ThePo1es force-pushed the fix-swap-scratch-slots-compatible branch from bd441a4 to 65f4a60 Compare September 7, 2026 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

boot_slots_compatible reads outside of sectors array bounds

1 participant