Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checks to ERC7579Utils.decodeBatch #5353

Merged
merged 28 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions contracts/account/utils/draft-ERC7579Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,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 @@ -73,8 +76,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 @@ -91,8 +94,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 @@ -169,12 +172,38 @@ 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) {
frangio marked this conversation as resolved.
Show resolved Hide resolved
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.
uint256 offset = uint256(bytes32(executionCalldata[0:32]));
ernestognw marked this conversation as resolved.
Show resolved Hide resolved

// The array length should be found at offset and 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 (offset > bufferLength - 32) revert ERC7579DecodingError();

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

// Get the array as a bytes slice, and check it is long enough:
// - 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.
// - `arrayLength * 32` does not overflow because `arrayLength` is less than `2**64`.
// Since we know bufferLength is at least offset + 32, we can subtract with no overflow risk.
if (arrayLength > type(uint64).max || bufferLength - offset - 32 < arrayLength * 32)
revert ERC7579DecodingError();

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

Expand Down
Loading
Loading