@@ -62,10 +62,13 @@ library ERC7579Utils {
62
62
/// @dev The module type is not supported.
63
63
error ERC7579UnsupportedModuleType (uint256 moduleTypeId );
64
64
65
+ /// @dev Input calldata not properly formatted and possibly malicious.
66
+ error ERC7579DecodingError ();
67
+
65
68
/// @dev Executes a single call.
66
69
function execSingle (
67
- ExecType execType ,
68
- bytes calldata executionCalldata
70
+ bytes calldata executionCalldata ,
71
+ ExecType execType
69
72
) internal returns (bytes [] memory returnData ) {
70
73
(address target , uint256 value , bytes calldata callData ) = decodeSingle (executionCalldata);
71
74
returnData = new bytes [](1 );
@@ -74,8 +77,8 @@ library ERC7579Utils {
74
77
75
78
/// @dev Executes a batch of calls.
76
79
function execBatch (
77
- ExecType execType ,
78
- bytes calldata executionCalldata
80
+ bytes calldata executionCalldata ,
81
+ ExecType execType
79
82
) internal returns (bytes [] memory returnData ) {
80
83
Execution[] calldata executionBatch = decodeBatch (executionCalldata);
81
84
returnData = new bytes [](executionBatch.length );
@@ -92,8 +95,8 @@ library ERC7579Utils {
92
95
93
96
/// @dev Executes a delegate call.
94
97
function execDelegateCall (
95
- ExecType execType ,
96
- bytes calldata executionCalldata
98
+ bytes calldata executionCalldata ,
99
+ ExecType execType
97
100
) internal returns (bytes [] memory returnData ) {
98
101
(address target , bytes calldata callData ) = decodeDelegate (executionCalldata);
99
102
returnData = new bytes [](1 );
@@ -170,12 +173,40 @@ library ERC7579Utils {
170
173
}
171
174
172
175
/// @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.
173
178
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
+ }
179
210
}
180
211
}
181
212
0 commit comments