From 9539463dcd6a8c245edaf55e9b26958ec6cec688 Mon Sep 17 00:00:00 2001 From: insuline Date: Thu, 8 Feb 2024 19:07:35 +0300 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20sanitizer=20for=20mu?= =?UTF-8?q?lticall?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SMv3SessionValidationModule.sol | 71 +++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/src/SMv3SessionValidationModule.sol b/src/SMv3SessionValidationModule.sol index 58d4221..42e6aee 100644 --- a/src/SMv3SessionValidationModule.sol +++ b/src/SMv3SessionValidationModule.sol @@ -47,12 +47,17 @@ contract SMv3SessionValidationModule is ISessionValidationModule { bytes4 funcSelector = bytes4(_funcCallData[0:4]); // sanitize the selector; ensure it is a valid selector - // that can be called on the smv3Engine) - _sanitizeSelector(funcSelector); + // that can be called on the smv3Engine + if (funcSelector == IEngine.multicall.selector) { + _decomposeAndSanitizeMulticallData(_funcCallData, callValue); + } else { + _sanitizeSelector(funcSelector); + + // sanitize the call value; ensure it is zero unless calling + // IEngine.depositEth or EIP7412.fulfillOracleQuery + _sanitizeCallValue(funcSelector, callValue); + } - // sanitize the call value; ensure it is zero unless calling - // IEngine.depositEth or EIP7412.fulfillOracleQuery - _sanitizeCallValue(funcSelector, callValue); return sessionKey; } @@ -114,10 +119,6 @@ contract SMv3SessionValidationModule is ISessionValidationModule { // that can be called on the smv3Engine) _sanitizeSelector(funcSelector); - // sanitize the call value; ensure it is zero unless calling - // IEngine.depositEth or EIP7412.fulfillOracleQuery - _sanitizeCallValue(funcSelector, callValue); - /// @dev this method of signature validation is out-of-date /// see https://github.com/OpenZeppelin/openzeppelin-sdk/blob/7d96de7248ae2e7e81a743513ccc617a2e6bba21/packages/lib/contracts/cryptography/ECDSA.sol#L6 return ECDSA.recover( @@ -135,15 +136,12 @@ contract SMv3SessionValidationModule is ISessionValidationModule { && _selector != IEngine.commitOrder.selector && _selector != IEngine.invalidateUnorderedNonces.selector && _selector != EIP7412.fulfillOracleQuery.selector - && _selector != IEngine.depositEth.selector - && _selector != IEngine.withdrawEth.selector ) { revert InvalidSMv3Selector(); } } - /// @notice sanitize the call value to ensure it is zero unless calling - /// IEngine.depositEth or EIP7412.fulfillOracleQuery + /// @notice sanitize the call value to ensure it is zero unless calling EIP7412.fulfillOracleQuery /// @param _selector the selector to sanitize /// @dev will revert if the call value is not valid function _sanitizeCallValue(bytes4 _selector, uint256 _callValue) @@ -151,14 +149,51 @@ contract SMv3SessionValidationModule is ISessionValidationModule { pure { if ( - _selector == IEngine.depositEth.selector - || _selector == EIP7412.fulfillOracleQuery.selector + _selector == EIP7412.fulfillOracleQuery.selector ) { - if (_callValue == 0) { + if (_callValue != 0) { revert InvalidCallValue(); } - } else if (_callValue != 0) { - revert InvalidCallValue(); + } + } + + /** + * @dev Processes and validates multicall data arrays, ensuring each call's selector is permitted and checks if + * `EIP7412.fulfillOracleQuery` is called, then validates the call value accordingly. + * + * + * @param _callData the data for the call. is parsed inside the SVM + * @param _callValue value to be sent with the call + * + * Note: This function does not return a value but may revert if it encounters an invalid function selector + * within the multicall data or if the call value does not comply with the prescribed validation rules, + * ensuring the contract's operational parameters are strictly adhered to. + */ + function _decomposeAndSanitizeMulticallData( + bytes calldata _callData, + uint256 _callValue + ) + internal + pure + { + bytes[] memory multicallData = abi.decode(_callData[4:], (bytes[])); + + bool hasFulfillOracleQueryCall = false; + + for (uint256 i = 0; i < multicallData.length; i++) { + bytes4 selector = bytes4(abi.encodePacked(multicallData[i])[:4]); + + _sanitizeSelector(selector); + + if (selector == EIP7412.fulfillOracleQuery.selector) { + hasFulfillOracleQueryCall = true; + } + } + + if (hasFulfillOracleQueryCall) { + _sanitizeCallValue(EIP7412.fulfillOracleQuery.selector, _callValue); + } else { + _sanitizeCallValue(IEngine.multicall.selector, _callValue); } } }