Skip to content

Commit

Permalink
Add checks to ERC7579Utils.decodeBatch (#5353)
Browse files Browse the repository at this point in the history
Co-authored-by: Ernesto García <[email protected]>
Co-authored-by: Francisco Giordano <[email protected]>
Signed-off-by: Hadrien Croubois <[email protected]>
  • Loading branch information
3 people committed Dec 13, 2024
1 parent f838dec commit 3fa7f94
Show file tree
Hide file tree
Showing 3 changed files with 477 additions and 25 deletions.
53 changes: 42 additions & 11 deletions contracts/account/utils/draft-ERC7579Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ library ERC7579Utils {
/// @dev The module type is not supported.
error ERC7579UnsupportedModuleType(uint256 moduleTypeId);

/// @dev Input calldata not properly formatted and possibly malicious.
error ERC7579DecodingError();

/// @dev Executes a single call.
function execSingle(
ExecType execType,
bytes calldata executionCalldata
bytes calldata executionCalldata,
ExecType execType
) internal returns (bytes[] memory returnData) {
(address target, uint256 value, bytes calldata callData) = decodeSingle(executionCalldata);
returnData = new bytes[](1);
Expand All @@ -74,8 +77,8 @@ library ERC7579Utils {

/// @dev Executes a batch of calls.
function execBatch(
ExecType execType,
bytes calldata executionCalldata
bytes calldata executionCalldata,
ExecType execType
) internal returns (bytes[] memory returnData) {
Execution[] calldata executionBatch = decodeBatch(executionCalldata);
returnData = new bytes[](executionBatch.length);
Expand All @@ -92,8 +95,8 @@ library ERC7579Utils {

/// @dev Executes a delegate call.
function execDelegateCall(
ExecType execType,
bytes calldata executionCalldata
bytes calldata executionCalldata,
ExecType execType
) internal returns (bytes[] memory returnData) {
(address target, bytes calldata callData) = decodeDelegate(executionCalldata);
returnData = new bytes[](1);
Expand Down Expand Up @@ -170,12 +173,40 @@ library ERC7579Utils {
}

/// @dev Decodes a batch of executions. See {encodeBatch}.
///
/// NOTE: This function runs some checks and will throw a {ERC7579DecodingError} if the input is not properly formatted.
function decodeBatch(bytes calldata executionCalldata) internal pure returns (Execution[] calldata executionBatch) {
assembly ("memory-safe") {
let ptr := add(executionCalldata.offset, calldataload(executionCalldata.offset))
// Extract the ERC7579 Executions
executionBatch.offset := add(ptr, 32)
executionBatch.length := calldataload(ptr)
unchecked {
uint256 bufferLength = executionCalldata.length;

// Check executionCalldata is not empty.
if (bufferLength < 32) revert ERC7579DecodingError();

// Get the offset of the array (pointer to the array length).
uint256 arrayLengthPointer = uint256(bytes32(executionCalldata[0:32]));

// The array length (at arrayLengthPointer) should be 32 bytes long. We check that this is within the
// buffer bounds. Since we know bufferLength is at least 32, we can subtract with no overflow risk.
if (arrayLengthPointer > bufferLength - 32) revert ERC7579DecodingError();

// Get the array length. arrayLengthPointer + 32 is bounded by bufferLength so it does not overflow.
uint256 arrayLength = uint256(bytes32(executionCalldata[arrayLengthPointer:arrayLengthPointer + 32]));

// Check that the buffer is long enough to store the array elements as "offset pointer":
// - each element of the array is an "offset pointer" to the data.
// - each "offset pointer" (to an array element) takes 32 bytes.
// - validity of the calldata at that location is checked when the array element is accessed, so we only
// need to check that the buffer is large enough to hold the pointers.
//
// Since we know bufferLength is at least arrayLengthPointer + 32, we can subtract with no overflow risk.
// Solidity limits length of such arrays to 2**64-1, this guarantees `arrayLength * 32` does not overflow.
if (arrayLength > type(uint64).max || bufferLength - arrayLengthPointer - 32 < arrayLength * 32)
revert ERC7579DecodingError();

assembly ("memory-safe") {
executionBatch.offset := add(add(executionCalldata.offset, arrayLengthPointer), 32)
executionBatch.length := arrayLength
}
}
}

Expand Down
Loading

0 comments on commit 3fa7f94

Please sign in to comment.