## Summary
The [fallback](https://github.com/Cyfrin/2024-07-biconomy/blob/9590f25cd63f7ad2c54feb618036984774f3879d/contracts/base/ModuleManager.sol#L72) function directly manipulates memory without explicit allocation. This can lead to memory overlap or corruption, causing potential vulnerabilities and unexpected behavior in the smart contract.
## Vulnerability Details
Direct Memory Manipulation: The uses of direct memory operations without allocating specific memory spaces. This involves copying calldata directly to memory and storing the caller address immediately after the calldata without explicit memory allocation.
```javascript
fallback() external payable override(Receiver) receiverFallback {
FallbackHandler storage $fallbackHandler = _getAccountStorage().fallbacks[msg.sig];
address handler = $fallbackHandler.handler;
CallType calltype = $fallbackHandler.calltype;
require(handler != address(0), MissingFallbackHandler(msg.sig));
if (calltype == CALLTYPE_STATIC) {
assembly {
-> calldatacopy(0, 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
-> mstore(calldatasize(), shl(96, caller()))
if iszero(staticcall(gas(), handler, 0, add(calldatasize(), 20), 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
returndatacopy(0, 0, returndatasize())
return(0, returndatasize())
}
}
if (calltype == CALLTYPE_SINGLE) {
assembly {
-> calldatacopy(0, 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
-> mstore(calldatasize(), shl(96, caller()))
if iszero(call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
returndatacopy(0, 0, returndatasize())
return(0, returndatasize())
}
}
}
```
Calldata Copy: calldatacopy(0, 0, calldatasize()) copies calldata to memory starting at position 0.
Caller Address Storage: mstore(calldatasize(), shl(96, caller())) stores the caller address at the position immediately following the calldata.
When the `calldata` is copied to `memory` and the caller `address` is stored right after it, there is a risk of memory overlap if subsequent memory operations do not manage the allocated memory correctly.
<details>
<summary>Example</summary>
Let's assume the calldata is a series of 32 bytes of zeroes (for simplicity) and the attacker wants to manipulate the fallback logic.
### Vulnerable Fallback Execution:
1. **Calldata Copy:**
```javascript
calldatacopy(0, 0, calldatasize())
```
- Copies 32 bytes of zeroes to memory starting at position 0.
2. **Caller Address Storage:**
```javascript
mstore(calldatasize(), shl(96, caller()))
```
- If `calldatasize()` is 32, it stores the caller address (let's say `0x1234567890abcdef1234567890abcdef12345678`) at position 32.
### Memory Layout:
- Memory from position 0 to 31: Calldata (32 bytes of zeroes).
- Memory from position 32 to 51: Caller address (`0x1234567890abcdef1234567890abcdef12345678`).
### Exploit Impact:
- **Memory Overlap:** If the calldata size or the operations performed are such that they overlap with other memory regions or critical data, this could lead to unexpected behavior. For example, if the contract logic relies on data stored immediately after the calldata, it could be corrupted by the caller's address.
### Potential Exploit Outcome:
- **Data Corruption:** An attacker could craft the calldata in such a way that it overlaps with memory regions used for storing important contract state or return data, leading to corrupted state or incorrect return values.
- **Reentrancy Attack:** If the corrupted memory includes control flow data or function pointers, it could potentially lead to reentrancy vulnerabilities where the attacker gains control over the contract's execution flow.
</details>
## Impact
Memory Overlap: Without proper memory allocation, there is a risk of overlapping memory regions, leading to data corruption. This can cause the smart contract to behave unexpectedly or fail during execution.
## Tools Used
Manual Review
## Recommendations
By using a memory allocation function, you can prevent such overlaps:
```javascript
assembly {
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
let calldataPtr := allocate(calldatasize())
calldatacopy(calldataPtr, 0, calldatasize())
let senderPtr := allocate(20)
mstore(senderPtr, shl(96, caller()))
let success := staticcall(gas(), handler, calldataPtr, add(calldatasize(), 20), 0, 0)
let returnDataPtr := allocate(returndatasize())
returndatacopy(returnDataPtr, 0, returndatasize())
if iszero(success) {
revert(returnDataPtr, returndatasize())
}
return(returnDataPtr, returndatasize())
}
```