Skip to content

Add test for RLP when encoding a payload with length >= 2**64#6343

Closed
ernestognw wants to merge 1 commit intoOpenZeppelin:masterfrom
ernestognw:audit/n-01
Closed

Add test for RLP when encoding a payload with length >= 2**64#6343
ernestognw wants to merge 1 commit intoOpenZeppelin:masterfrom
ernestognw:audit/n-01

Conversation

@ernestognw
Copy link
Member

Fixes #????

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

@ernestognw ernestognw requested a review from a team as a code owner February 11, 2026 00:55
@openzeppelin-security-agent
Copy link

The latest updates on your security scan. Learn more about OpenZeppelin Platform.

Project Scan Issues Details Updated
Openzeppelin Contracts 🟡 Queued View Feb 11, 2026, 12:56 AM (UTC)

@changeset-bot
Copy link

changeset-bot bot commented Feb 11, 2026

⚠️ No Changeset found

Latest commit: 1ac063a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

Walkthrough

A new test case was added to test/utils/RLP.test.js that validates the behavior of RLP encoding when processing a payload with an oversized length field (≥ 2^64). The test constructs specific calldata for the encode(bytes) function with an oversized length and a long-form path, expecting an out-of-gas revert. The test appears at two locations in the diff but both validate the same out-of-gas condition.

Possibly related PRs

Suggested labels

tests, ignore-changeset

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a test for RLP encoding with payload length >= 2**64.
Description check ✅ Passed The description is the standard repository template without specific details, but it is related to the changeset as it references testing requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/utils/RLP.test.js`:
- Around line 208-216: The test's description ("length >= 2**64") doesn't match
the value used (MAX_UINT64 == 2**64 - 1) in the RLP.test.js case; either update
the forged length to test the >= 2**64 boundary by using MAX_UINT64 + 1 (so the
encoded length is >= 2**64) in the data construction that uses MAX_UINT64, or
keep the current numeric value and change the it(...) title/description to
accurately state "length == 2**64 - 1" or "length close to 2**64"; locate the
test by the it(...) string and the usage of MAX_UINT64 in the data variable to
apply the fix.

Comment on lines +208 to +216
it('reverts out of gas when encoding payload with length >= 2**64', async function () {
const data = ethers.concat([
this.mock.interface.getFunction('$encode(bytes)').selector,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0x20']), // offset to bytes
ethers.toBeHex(MAX_UINT64, 32), // forged length
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [56]), // long-form path (length > 55)
]);
await expect(this.mock.runner.sendTransaction({ to: this.mock.target, data })).to.be.revertedWithoutReason();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Value mismatch: MAX_UINT64 is 2^64 - 1, not >= 2^64.

The test description and PR title reference "length >= 2**64", but MAX_UINT64 equals 2^64 - 1, which is strictly less than 2^64. If the intent is to test the boundary condition at >= 2^64, consider using MAX_UINT64 + 1n:

-      ethers.toBeHex(MAX_UINT64, 32), // forged length
+      ethers.toBeHex(MAX_UINT64 + 1n, 32), // forged length >= 2**64

If MAX_UINT64 is the intended value (testing the max 64-bit length), then the test description should be updated to reflect "length close to 2^64" or "length == 2^64 - 1".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('reverts out of gas when encoding payload with length >= 2**64', async function () {
const data = ethers.concat([
this.mock.interface.getFunction('$encode(bytes)').selector,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0x20']), // offset to bytes
ethers.toBeHex(MAX_UINT64, 32), // forged length
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [56]), // long-form path (length > 55)
]);
await expect(this.mock.runner.sendTransaction({ to: this.mock.target, data })).to.be.revertedWithoutReason();
});
it('reverts out of gas when encoding payload with length >= 2**64', async function () {
const data = ethers.concat([
this.mock.interface.getFunction('$encode(bytes)').selector,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0x20']), // offset to bytes
ethers.toBeHex(MAX_UINT64 + 1n, 32), // forged length >= 2**64
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [56]), // long-form path (length > 55)
]);
await expect(this.mock.runner.sendTransaction({ to: this.mock.target, data })).to.be.revertedWithoutReason();
});
🤖 Prompt for AI Agents
In `@test/utils/RLP.test.js` around lines 208 - 216, The test's description
("length >= 2**64") doesn't match the value used (MAX_UINT64 == 2**64 - 1) in
the RLP.test.js case; either update the forged length to test the >= 2**64
boundary by using MAX_UINT64 + 1 (so the encoded length is >= 2**64) in the data
construction that uses MAX_UINT64, or keep the current numeric value and change
the it(...) title/description to accurately state "length == 2**64 - 1" or
"length close to 2**64"; locate the test by the it(...) string and the usage of
MAX_UINT64 in the data variable to apply the fix.

@ernestognw ernestognw added this to the 5.6 milestone Feb 11, 2026
Comment on lines +210 to +213
this.mock.interface.getFunction('$encode(bytes)').selector,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0x20']), // offset to bytes
ethers.toBeHex(MAX_UINT64, 32), // forged length
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [56]), // long-form path (length > 55)
Copy link
Collaborator

@Amxx Amxx Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, this would be a valid input that would correspond to a buffer of size 264, and not 264-1.

Suggested change
this.mock.interface.getFunction('$encode(bytes)').selector,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0x20']), // offset to bytes
ethers.toBeHex(MAX_UINT64, 32), // forged length
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [56]), // long-form path (length > 55)
this.mock.interface.getFunction('$encode(bytes)').selector,
ethers.toBeHex('0x20, 32), // offset to bytes
ethers.toBeHex(MAX_UINT64 + 1n, 32), // forged length

Would still not properly test the function, because it OOG in the calldata to memory copy BEFORE the any of the encode(bytes) internal logic is reached.

@ernestognw
Copy link
Member Author

Closing. After discussing we agreed that the test is not relevant for the code we want to test because the function is not reached and Solidity reverts copying the calldata to memory.

@ernestognw ernestognw closed this Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants