HardhatFoundry
30,000 USDC
View results
Submission Details
Severity: low
Invalid

Inability to track failures in `Nexus::execute()` when executed in `EXECTYPE_TRY` type

Summary

The Nexus::execute() handle the execution but does not emit the result when the execution with the EXECTYPE_TRY type fails.
As a result this lack of emission makes it untrackable when EXECTYPE_TRY executions fail.

Vulnerability Details

The Nexus::execute() uses the

  • ExecutionHelper::_handleSingleExecution(),

  • ExecutionHelper::_handleBatchExecution()

  • ExecutionHelper::_handleDelegateCallExecution()

to handle executions that allows 2 execution types: EXECTYPE_DEFAULT (revert on failure) and EXECTYPE_TRY (allow failure).

\\Location Nexus.sol::execute()
function execute(ExecutionMode mode, bytes calldata executionCalldata) external payable onlyEntryPointOrSelf withHook {
(CallType callType, ExecType execType) = mode.decodeBasic();
if (callType == CALLTYPE_SINGLE) {
@> _handleSingleExecution(executionCalldata, execType);
} else if (callType == CALLTYPE_BATCH) {
@> _handleBatchExecution(executionCalldata, execType);
} else if (callType == CALLTYPE_DELEGATECALL) {
@> _handleDelegateCallExecution(executionCalldata, execType);
} else {
revert UnsupportedCallType(callType);
}
}

However, when executed with EXECTYPE_TRY, the functions _handleSingleExecution() and _handleDelegateCallExecution() do not handle or emit success conditions or return data for the call.

\\Location ExecutionHelper.sol::_handleSingleExecution()
function _tryExecute(address target, uint256 value, bytes calldata callData) internal virtual returns (bool success, bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
success := call(gas(), target, value, result, callData.length, codesize(), 0x00)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
// snipped
function _handleSingleExecution(bytes calldata executionCalldata, ExecType execType) internal {
(address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle();
if (execType == EXECTYPE_DEFAULT) _execute(target, value, callData);
@> else if (execType == EXECTYPE_TRY) _tryExecute(target, value, callData);
else revert UnsupportedExecType(execType);
}
\\Location ExecutionHelper.sol::_handleDelegateCallExecution()
function _tryExecuteDelegatecall(address delegate, bytes calldata callData) internal returns (bool success, bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
// Forwards the `data` to `delegate` via delegatecall.
success := delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
// snipped
function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) internal {
(address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall();
if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData);
@> else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData);
else revert UnsupportedExecType(execType);
}

Impact

Without emitted results, tracking and debugging execution failures become challenging, especially when EntryPoint or Smart Accounts themselves utilize the Nexus::execute() with the EXECTYPE_TRY type.

Tools Used

Manual Review

Recommendations

  • Modify _handleSingleExecution() and _handleDelegateCallExecution() functions in ExecutionHelper.sol to emit events for failure scenarios when using EXECTYPE_TRY.

  • Consider using _handleSingleExecutionAndReturnData(), _handleBatchExecutionAndReturnData(), and _handleDelegateCallExecutionAndReturnData() instead, as these functions already implement emission for failure scenarios.

Updates

Lead Judging Commences

0xnevi Lead Judge
11 months ago
0xnevi Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.