boot: bootutil: bounds-check TLV length in tlv iterator#2733
Open
d3zd3z wants to merge 1 commit into
Open
Conversation
`bootutil_tlv_iter_next()` reads a TLV header from flash and advances `it->tlv_off` by `sizeof(tlv) + tlv.it_len` without checking that the header and its payload actually lie within the TLV section. `tlv.it_len` is a `uint16_t` taken straight from flash and is attacker-controlled in a crafted image, so a bogus length could advance the iterator past `it->tlv_end` and hand the caller an `(off, len)` pair pointing outside the TLV section. Every current consumer in `image_validate.c` already validates `len` against a small fixed buffer before reading, so this is a defense-in-depth / robustness fix rather than an exploitable vulnerability. The check makes the iterator self-contained instead of relying on every caller to sanity-check `len`, and converts silent early termination on a malformed image into an explicit error. Malformed TLV lengths now produce an explicit iteration error instead of silently truncating iteration. Assisted-By: Claude:opus-4.7 Signed-off-by: David Brown <david.brown@linaro.org>
There was a problem hiding this comment.
Pull request overview
Adds TLV iterator validation intended to reject malformed TLV entries whose payload would extend beyond the declared TLV section.
Changes:
- Adds length/bounds validation in
bootutil_tlv_iter_next(). - Logs and returns an explicit iteration error for malformed TLV sizes.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+143
to
+145
| if (it->tlv_off > it->tlv_end || | ||
| (uint32_t)sizeof(tlv) > it->tlv_end - it->tlv_off || | ||
| tlv.it_len > it->tlv_end - it->tlv_off - (uint32_t)sizeof(tlv)) { |
Comment on lines
+143
to
+145
| if (it->tlv_off > it->tlv_end || | ||
| (uint32_t)sizeof(tlv) > it->tlv_end - it->tlv_off || | ||
| tlv.it_len > it->tlv_end - it->tlv_off - (uint32_t)sizeof(tlv)) { |
Comment on lines
+143
to
+145
| if (it->tlv_off > it->tlv_end || | ||
| (uint32_t)sizeof(tlv) > it->tlv_end - it->tlv_off || | ||
| tlv.it_len > it->tlv_end - it->tlv_off - (uint32_t)sizeof(tlv)) { |
de-nordic
approved these changes
May 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a bounds check to
bootutil_tlv_iter_next()so that a TLV header and its payload are verified to lie fully within the TLV section before the iterator advances or hands an(off, len)pair to the caller.tlv.it_lenis auint16_tread straight from flash and is attacker-controlled in a crafted image. Without this check, a bogus length advancesit->tlv_offpastit->tlv_endand yields an out-of-section(off, len)pair.Severity / framing
This is a defense-in-depth / robustness fix, not a critical vulnerability:
image_validate.calready validateslenagainst a small fixed buffer before reading (hash, key, signature, security counter, UUID), so a bogusit_lendoes not currently cause a large out-of-bounds read or a buffer overflow.while (it->tlv_off < it->tlv_end)loop early; malformed TLVs cause validation to fail closed, not to be bypassed.The value of the fix is that it makes the iterator self-contained and correct instead of relying on every current and future caller to sanity-check
len, and it converts silent early-termination on a malformed image into an explicit error.Behavioural change
Malformed TLV lengths now produce an explicit iteration error (
-1) instead of silently truncating iteration. For valid images the check is a no-op.The check
The three clauses short-circuit left-to-right so the
uint32_tarithmetic can neither underflow nor wrap: clause 1 guards the subtraction in the later clauses, and clause 2 ensuressizeof(tlv)fits before clause 3 subtracts it.Testing
Existing sim suite passes unchanged with
sig-rsa,sig-ecdsa, andsig-ecdsa,swap-offset(25 tests each). The check is a no-op for any well-formed image, so no behavioural change is expected for valid images. No dedicated negative test is added: the prior behaviour was already fail-closed, so a crafted-image test would exercise a path whose observable outcome is unchanged.