Full Report
The tweet starts with an image of Solidity code: here. It's mostly Yul Assembly with two functions calls in it. The first function g(), which calls storeAndReturn(). Inside of storeAndReturn() is a Yul assembly block with assembly {return(0,0)}. In most languages, return exits the function. However, in Yul, (unlike standard Solidity), this stops execution of the contract at that moment instead of simply returning to the next function. I ran into this once and thought it was extremely weird. The author just learned this. While looking at an Immunefi program, they noticed a function called functionCallWithValue within a library. This was calling return in Yul thinking that it was returning back to the function. Instead, it was completely ending execution. What if additionally checks or calls need to be made? This could lead to a security issue. In the case of this program, they didn't find anything directly exploitable. Instead, a user using batchExecute() would have unexpected results because of this functionality. They got 5K for finding this bug.
Analysis Summary
# Vulnerability: Misuse of Yul `return` Opcode Leading to Unexpected Execution Termination
## CVE Details
- **CVE ID**: N/A (Discovered via Bug Bounty; no CVE assigned)
- **CVSS Score**: N/A (Bounty payout: $5,000 USD)
- **CWE**: CWE-670: Always-Incorrect Control Flow Implementation
## Affected Systems
- **Products**: Smart Contracts utilizing Solidity with inline Yul Assembly.
- **Versions**: All Solidity versions supporting inline assembly (Yul).
- **Configurations**: Contracts employing architectural patterns like `batchExecute()` or multi-call libraries (e.g., `functionCallWithValue`) where Yul `return` is mistakenly used to exit a sub-function rather than the entire execution context.
## Vulnerability Description
In standard Solidity, the `return` keyword exits the current function and returns control to the caller. However, within a Yul `assembly` block, the `return(offset, size)` opcode behaves differently: it halts the **entire execution context** (the internal transaction) and returns data from the specified memory plateau.
The vulnerability arises when a developer uses `return` inside a Yul block within a helper function or library, expecting it to return to the parent Solidity function. Instead, it terminates the entire call. If this helper is used within a loop (like a batch executor) or before critical state updates/security checks, those subsequent operations are silently skipped, leading to incomplete execution or "stuck" funds/state.
## Exploitation
- **Status**: PoC demonstrated (Bug discovered via Immunefi program). No evidence of exploitation in the wild.
- **Complexity**: Medium (Requires understanding of Yul execution flow and contract architecture).
- **Attack Vector**: Network (Smart Contract Interaction).
## Impact
- **Confidentiality**: None
- **Integrity**: Medium/High (Logical bypass; subsequent state updates or mandatory security checks following the Yul block are never reached).
- **Availability**: High (Unexpected termination of batch transactions; functions failing to complete their intended logic).
## Remediation
### Patches
- Ensure the contract logic does not rely on subsequent code execution after a Yul `return` opcode is called.
- Replace `return(0,0)` in Yul with `leave` if the intention is to exit the current functional block (available in Yul functions) or simply let the assembly block reach its end to return control to the Solidity wrapper.
### Workarounds
- Use standard Solidity `return` instead of inline assembly for simple return tasks.
- If using Yul, ensure the assembly block is the final operation in the function call chain to prevent skipping critical logic.
## Detection
- **Indicators of Compromise**: Transactions involving batch execution or multicalls failing to execute all items despite a "Success" status, or state changes being partially applied.
- **Detection methods and tools**:
- **Static Analysis**: Slither or Mythril can be configured to flag `return` opcodes inside Yul blocks that are not at the end of the transaction flow.
- **Manual Audit**: Reviewing all `assembly { return(...) }` instances to confirm they do not unintentionally bypass subsequent logic or checks.
## References
- **MiloTruck X Thread**: hxxps://x[.]com/milotruck/status/1696809536123007336
- **Solidity Documentation (Yul)**: hxxps://docs[.]soliditylang[.]org/en/latest/yul[.]html
- **Immunefi**: hxxps://immunefi[.]com/