📄

Report #22316

Report Date
July 18, 2023
Status
Closed
Payout

Low-level transfer via call() can fail silently

‣
Report Info

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.

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.