LibBytes: bound s in dynamicStructInCalldata - #1548
Open
Popy21 wants to merge 1 commit into
Open
Conversation
`dynamicStructInCalldata` reads the relative offset `s` from calldata and
computes `result.length := sub(a.length, s)` without bounding `s` against
`a.length`. When `s > a.length` the subtraction underflows and the helper
returns a slice whose offset is past the end of `a` and whose length is
~2**256. The existing `shr(64, s)` term does not catch it: a value slightly
larger than `a.length` is far below 2**64.
The two sibling helpers in the same file already reject the same input --
`staticStructInCalldata` via `gt(offset, l)` and `bytesInCalldata` via
`gt(add(s, result.length), l)`. This adds the matching bound.
Adds a regression test that fails without the change
("next call did not revert as expected") and passes with it.
Full test/LibBytes.t.sol suite: 24/24 passing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Problem
dynamicStructInCalldatareads the relative offsetsfrom calldata and computesresult.length := sub(a.length, s)without ever boundingsagainsta.length.When
s > a.lengththe subtraction underflows. The existingshr(64, s)term does not catch it — avalue slightly larger than
a.lengthis far below2**64. The helper then returns a slice whoseoffset is past the end of
aand whose length is~2**256:loadCalldataperforms no bounds check of its own, so a caller following the documented patternreads outside the buffer. The doc comment states "Performs bounds checks".
The two sibling helpers in the same file already reject the same input:
staticStructInCalldatabounds viagt(offset, l),bytesInCalldataviagt(add(s, result.length), l). OnlydynamicStructInCalldatais missing the bound.Change
One added term in the existing revert condition:
Verification
testDynamicStructInCalldataRejectsOutOfBoundsOffset, in the style of the file.next call did not revert as expected) and passes with it,so it is not vacuous.
test/LibBytes.t.solsuite: 24/24 passing, including the existingtestDynamicStructInCalldatafuzz tests, so well-formed inputs are unaffected.Note for reviewers
Found while auditing calldata-slice construction across ERC-7579 decoders. Filed as a hardening PR
rather than through a security channel: there is no in-repo caller, and the two external call sites I
found are not exploitable through this path —
Uniswap/Tribunalpins the relevant offset to0x60before use and is not deployed, and
ithacaxyz/accountfollows every extraction withcheckInCalldata, whose upper bound self-corrects under the underflow(
(E.length - s) + (E.offset + s) == E.offset + E.length). Filing it as robustness so the helpermatches its own documentation and its siblings.
🤖 Generated with Claude Code