Low-level transfer via call() can fail silently
Report ID
#22316
Report type
Smart Contract
Has PoC?
Yes
Target
Impacts
- Contract fails to deliver promised returns, but doesn't lose value
Bug Description
In the contract Pipeline.sol, the function _pipe() is using the low level .call() function.
(success, result) = target.call{value: value}(data);
The function does not check for account existence.
The Solidity documentation warns: "The low-level functions call, delegatecall and staticcall return true as their first return value if the account called is non-existent, as part of the design of the EVM. Account existence must be checked prior to calling if needed".
As a result, if a transaction is meant to execute code, and data is non-empty, the transaction will succeed even if the destination has no code, or is destructed.
This behavior might lead the caller to assume the success of some operations, even though nothing was executed.
Impact
The function _pipe() is used to execute the external function pipe() and multipipe().
Some transactions using these functions might succeed even if nothing was executed.
Exploit Scenario
Bob wants to execute a single PipeCall but he sets the the destination incorrectly. The transaction is successful instead of failing, and Bob does not notice that nothing happened.
Contract fails to deliver promised returns.
This issue concerns also the function _pipeMem():Â https://etherscan.io/address/0xb1bE0000C6B3C62749b5F0c92480146452D15423#code#F1#L99
Risk Breakdown
Difficulty to Exploit: Easy Weakness: CVSS2 Score:
Recommendation
Check for contract existence in Pipeline._pipe() and _pipeMem() if data is not empty. This will prevent the success of transactions that do not execute code.
References
Proof of concept
// Execute function call using calldata
function _pipe(
address target,
bytes calldata data,
uint256 value
) private returns (bytes memory result) {
bool success;
(success, result) = target.call{value: value}(data);
LibFunction.checkReturn(success, result);
}
// Execute function call using memory
function _pipeMem(
address target,
bytes memory data,
uint256 value
) private returns (bytes memory result) {
bool success;
(success, result) = target.call{value: value}(data);
LibFunction.checkReturn(success, result);
}
'Warning
The low-level functions call, delegatecall and staticcall return true as their first return value if the account called is non-existent, as part of the design of the EVM. Account existence must be checked prior to calling if needed.'
BIC Response
This is not a valid bug report because it reports expected behavior. Unexpected outcomes due to misuse of Pipeline do not qualify as valid bug reports.
Due to these reasons, we are closing the submission and no reward will be issued.