@@ -62,10 +62,13 @@ library ERC7579Utils {
6262 /// @dev The module type is not supported.
6363 error ERC7579UnsupportedModuleType (uint256 moduleTypeId );
6464
65+ /// @dev Input calldata not properly formatted and possibly malicious.
66+ error ERC7579DecodingError ();
67+
6568 /// @dev Executes a single call.
6669 function execSingle (
67- ExecType execType ,
68- bytes calldata executionCalldata
70+ bytes calldata executionCalldata ,
71+ ExecType execType
6972 ) internal returns (bytes [] memory returnData ) {
7073 (address target , uint256 value , bytes calldata callData ) = decodeSingle (executionCalldata);
7174 returnData = new bytes [](1 );
@@ -74,8 +77,8 @@ library ERC7579Utils {
7477
7578 /// @dev Executes a batch of calls.
7679 function execBatch (
77- ExecType execType ,
78- bytes calldata executionCalldata
80+ bytes calldata executionCalldata ,
81+ ExecType execType
7982 ) internal returns (bytes [] memory returnData ) {
8083 Execution[] calldata executionBatch = decodeBatch (executionCalldata);
8184 returnData = new bytes [](executionBatch.length );
@@ -92,8 +95,8 @@ library ERC7579Utils {
9295
9396 /// @dev Executes a delegate call.
9497 function execDelegateCall (
95- ExecType execType ,
96- bytes calldata executionCalldata
98+ bytes calldata executionCalldata ,
99+ ExecType execType
97100 ) internal returns (bytes [] memory returnData ) {
98101 (address target , bytes calldata callData ) = decodeDelegate (executionCalldata);
99102 returnData = new bytes [](1 );
@@ -170,12 +173,40 @@ library ERC7579Utils {
170173 }
171174
172175 /// @dev Decodes a batch of executions. See {encodeBatch}.
176+ ///
177+ /// NOTE: This function runs some checks and will throw a {ERC7579DecodingError} if the input is not properly formatted.
173178 function decodeBatch (bytes calldata executionCalldata ) internal pure returns (Execution[] calldata executionBatch ) {
174- assembly ("memory-safe" ) {
175- let ptr := add (executionCalldata.offset, calldataload (executionCalldata.offset))
176- // Extract the ERC7579 Executions
177- executionBatch.offset := add (ptr, 32 )
178- executionBatch.length := calldataload (ptr)
179+ unchecked {
180+ uint256 bufferLength = executionCalldata.length ;
181+
182+ // Check executionCalldata is not empty.
183+ if (bufferLength < 32 ) revert ERC7579DecodingError ();
184+
185+ // Get the offset of the array (pointer to the array length).
186+ uint256 arrayLengthOffset = uint256 (bytes32 (executionCalldata[0 :32 ]));
187+
188+ // The array length (at arrayLengthOffset) should be 32 bytes long. We check that this is within the
189+ // buffer bounds. Since we know bufferLength is at least 32, we can subtract with no overflow risk.
190+ if (arrayLengthOffset > bufferLength - 32 ) revert ERC7579DecodingError ();
191+
192+ // Get the array length. arrayLengthOffset + 32 is bounded by bufferLength so it does not overflow.
193+ uint256 arrayLength = uint256 (bytes32 (executionCalldata[arrayLengthOffset:arrayLengthOffset + 32 ]));
194+
195+ // Check that the buffer is long enough to store the array elements as "offset pointer":
196+ // - each element of the array is an "offset pointer" to the data.
197+ // - each "offset pointer" (to an array element) takes 32 bytes.
198+ // - validity of the calldata at that location is checked when the array element is accessed, so we only
199+ // need to check that the buffer is large enough to hold the pointers.
200+ //
201+ // Since we know bufferLength is at least arrayLengthOffset + 32, we can subtract with no overflow risk.
202+ // Solidity limits length of such arrays to 2**64-1, this guarantees `arrayLength * 32` does not overflow.
203+ if (arrayLength > type (uint64 ).max || bufferLength - arrayLengthOffset - 32 < arrayLength * 32 )
204+ revert ERC7579DecodingError ();
205+
206+ assembly ("memory-safe" ) {
207+ executionBatch.offset := add (add (executionCalldata.offset, arrayLengthOffset), 32 )
208+ executionBatch.length := arrayLength
209+ }
179210 }
180211 }
181212
0 commit comments