Skip to content

Commit 77d4a73

Browse files
Amxxernestognwfrangio
authored
Add checks to ERC7579Utils.decodeBatch (#5353)
Co-authored-by: Ernesto García <[email protected]> Co-authored-by: Francisco Giordano <[email protected]>
1 parent 5df1070 commit 77d4a73

File tree

3 files changed

+477
-25
lines changed

3 files changed

+477
-25
lines changed

contracts/account/utils/draft-ERC7579Utils.sol

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,13 @@ library ERC7579Utils {
6161
/// @dev The module type is not supported.
6262
error ERC7579UnsupportedModuleType(uint256 moduleTypeId);
6363

64+
/// @dev Input calldata not properly formatted and possibly malicious.
65+
error ERC7579DecodingError();
66+
6467
/// @dev Executes a single call.
6568
function execSingle(
66-
ExecType execType,
67-
bytes calldata executionCalldata
69+
bytes calldata executionCalldata,
70+
ExecType execType
6871
) internal returns (bytes[] memory returnData) {
6972
(address target, uint256 value, bytes calldata callData) = decodeSingle(executionCalldata);
7073
returnData = new bytes[](1);
@@ -73,8 +76,8 @@ library ERC7579Utils {
7376

7477
/// @dev Executes a batch of calls.
7578
function execBatch(
76-
ExecType execType,
77-
bytes calldata executionCalldata
79+
bytes calldata executionCalldata,
80+
ExecType execType
7881
) internal returns (bytes[] memory returnData) {
7982
Execution[] calldata executionBatch = decodeBatch(executionCalldata);
8083
returnData = new bytes[](executionBatch.length);
@@ -91,8 +94,8 @@ library ERC7579Utils {
9194

9295
/// @dev Executes a delegate call.
9396
function execDelegateCall(
94-
ExecType execType,
95-
bytes calldata executionCalldata
97+
bytes calldata executionCalldata,
98+
ExecType execType
9699
) internal returns (bytes[] memory returnData) {
97100
(address target, bytes calldata callData) = decodeDelegate(executionCalldata);
98101
returnData = new bytes[](1);
@@ -169,12 +172,40 @@ library ERC7579Utils {
169172
}
170173

171174
/// @dev Decodes a batch of executions. See {encodeBatch}.
175+
///
176+
/// NOTE: This function runs some checks and will throw a {ERC7579DecodingError} if the input is not properly formatted.
172177
function decodeBatch(bytes calldata executionCalldata) internal pure returns (Execution[] calldata executionBatch) {
173-
assembly ("memory-safe") {
174-
let ptr := add(executionCalldata.offset, calldataload(executionCalldata.offset))
175-
// Extract the ERC7579 Executions
176-
executionBatch.offset := add(ptr, 32)
177-
executionBatch.length := calldataload(ptr)
178+
unchecked {
179+
uint256 bufferLength = executionCalldata.length;
180+
181+
// Check executionCalldata is not empty.
182+
if (bufferLength < 32) revert ERC7579DecodingError();
183+
184+
// Get the offset of the array (pointer to the array length).
185+
uint256 arrayLengthPointer = uint256(bytes32(executionCalldata[0:32]));
186+
187+
// The array length (at arrayLengthPointer) should be 32 bytes long. We check that this is within the
188+
// buffer bounds. Since we know bufferLength is at least 32, we can subtract with no overflow risk.
189+
if (arrayLengthPointer > bufferLength - 32) revert ERC7579DecodingError();
190+
191+
// Get the array length. arrayLengthPointer + 32 is bounded by bufferLength so it does not overflow.
192+
uint256 arrayLength = uint256(bytes32(executionCalldata[arrayLengthPointer:arrayLengthPointer + 32]));
193+
194+
// Check that the buffer is long enough to store the array elements as "offset pointer":
195+
// - each element of the array is an "offset pointer" to the data.
196+
// - each "offset pointer" (to an array element) takes 32 bytes.
197+
// - validity of the calldata at that location is checked when the array element is accessed, so we only
198+
// need to check that the buffer is large enough to hold the pointers.
199+
//
200+
// Since we know bufferLength is at least arrayLengthPointer + 32, we can subtract with no overflow risk.
201+
// Solidity limits length of such arrays to 2**64-1, this guarantees `arrayLength * 32` does not overflow.
202+
if (arrayLength > type(uint64).max || bufferLength - arrayLengthPointer - 32 < arrayLength * 32)
203+
revert ERC7579DecodingError();
204+
205+
assembly ("memory-safe") {
206+
executionBatch.offset := add(add(executionCalldata.offset, arrayLengthPointer), 32)
207+
executionBatch.length := arrayLength
208+
}
178209
}
179210
}
180211

0 commit comments

Comments
 (0)