Commit message · tychovrahefix(core/bootloader): keep the full block length for block-0 retries
Block 0 is the only block fetched in two requests: an initial
IMAGE_INIT_CHUNK_SIZE prefetch so the headers can be validated, then the
remainder. `chunk_requested` was serving three roles at once -- request size,
expected buffer fill, and the amount still owed on `remaining` -- and after the
header prefetch it holds only the remainder.
A retry then asked for that remainder from offset 0. The size check compared
`chunk_size` against `chunk_requested + read_offset`, i.e. the remainder against
itself, so it passed and `on_chunk` received a truncated block: the first
`chunk_limit - IMAGE_INIT_CHUNK_SIZE` bytes sitting at buffer offset 0. The hash
then necessarily mismatched, so the retry budget burned down and the upload
aborted. Block-0 retries could never succeed -- on T3W1 that is a 256 KB block,
where a transient BLE error is most likely.
Replace `chunk_requested` with `chunk_expected`: the bytes the buffer must hold
for the chunk to be complete, i.e. the whole block. It is the only field of its
kind left -- every request is derived from it, `remaining` is decremented by what
actually arrived, and `read_offset` goes back to being purely a buffer write
offset. The retry re-fetches the whole block from its start, and the size check
compares against what the buffer must actually hold.
No behavioural change on the success path: the bytes asked for at each request
site are the same as before, and what the old check computed is exactly what
`chunk_expected` now holds.
Alongside, in the same code: the image-size validation becomes an early guard in
`run_image_upload`, which is what guarantees a full init chunk to prefetch;
`confirmed` folds into `headers_parsed`, both having been set at the same point;
and the result dispatch becomes a switch.
The same bug is in wf_firmware_update.c on main; this fix is written against the
extracted engine but applies there unchanged.
[no changelog]
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
85/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides detailed explanatory context
Why it was queuedupdate trustdefensive validationboot or update path
AI analysis · Low 45/100This commit fixes a bug in the Trezor bootloader's firmware-update code. When updating firmware, the first block of data is fetched in two pieces: a small initial 'header prefetch,' then the rest. If a communication error happened and the bootloader tried to retry, it accidentally asked for only the leftover remainder but placed it at the start of the buffer. That produced a corrupted block, the hash check failed, every retry failed, and the firmware update aborted. The fix makes retries request the whole first block again from the beginning. There is no evidence this bug was exploitable by an attacker; it appears to be a reliability defect that could brick or stall legitimate updates, especially over Bluetooth on the T3W1 model.