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

The `ExecutionHelper::_execute(address, uint256, bytes calldata)` function could lead to a free memory pointer pointing to an already written spot in memory

Summary

The ExecutionHelper::_execute(address, uint256, bytes calldata) function could lead to a free memory pointer pointing to an already written spot in memory, causing potential issues.

Vulnerability Details

During the execution of the UserOperation, the function ExecutionHelper::_execute(address target, uint256 value, bytes calldata callData) stores the callData passed as an argument in the free memory slot. After the low-level call, the returned data size overwrites the first memory slot where the callData was stored, and the second slot is overwritten by the returned data itself. At the end of the process, the free memory pointer is set to point to the slot after the returned data. However, if callData.length > returndatasize() + 0x20, then the free memory pointer will point to a part of the callData bytes, leading to potential misuse of memory.

Proof of Concept

Assume the free memory pointer at the beginning of the execution is equal to 0x80. We execute the ExecutionHelper::_execute(address target, uint256 value, bytes calldata callData) function with callData = 0xffffff...fffff (128 bytes long). Then, all slots from 0x80 to 0xc0 are written:

Memory
0x00 | 0x0000000000000000000000000000000000000000000000000000000000000000
0x20 | 0x0000000000000000000000000000000000000000000000000000000000000000
0x40 | 0x0000000000000000000000000000000000000000000000000000000000000080
0x60 | 0x0000000000000000000000000000000000000000000000000000000000000000
0x80 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
0xa0 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
0xc0 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Suppose the low-level call to the target with the given callData returns a uint256, so returndatasize() = 0x20. At the end of the execution, the free memory pointer will be pointing to 0x80 + 0x20 + returndatasize() = 0x80 + 0x20 + 0x20 = 0xc0.

Memory
0x00 | 0x0000000000000000000000000000000000000000000000000000000000000000
0x20 | 0x0000000000000000000000000000000000000000000000000000000000000000
0x40 | 0x00000000000000000000000000000000000000000000000000000000000000c0
0x60 | 0x0000000000000000000000000000000000000000000000000000000000000000
0x80 | 0x0000000000000000000000000000000000000000000000000000000000000020
0xa0 | 0x00000000000000000000000000000000000000000000000000000000deadbeef
0xc0 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Impact

The impact of this vulnerability could lead to unexpected memory behavior, potentially causing data corruption or unintended consequences within the smart contract execution flow.

Tools Used

Manual review

Recommendations

To prevent this misuse of memory, the callData should be removed from memory before any further operations:

function _execute(address target, uint256 value, bytes calldata callData) internal virtual returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) {
// Bubble up the revert if the call reverts.
returndatacopy(result, 0x00, returndatasize())
revert(result, returndatasize())
}
+ calldatacopy(result, calldatasize(), callData.length) // Delete the callData parameter from memory
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.
}
}

This modification ensures that callData is properly deleted from memory, preventing the free memory pointer from pointing to an already written spot in memory.

Updates

Lead Judging Commences

0xnevi Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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