Skip to main content
Sigvex

Returnbomb Remediation

How to defend against return-data bombs by bounding or discarding returndata from untrusted calls.

Returnbomb Remediation

Overview

Related Detector: Returnbomb

A “return bomb” is a hostile callee that returns a huge byte array. When the caller automatically copies returndata into memory — which the high-level (bool ok, bytes memory ret) = target.call(...) form does — memory expansion gas grows quadratically and can exhaust the gas forwarded, forcing a revert. In relayers, multicall, and try/catch flows this lets one target grief the whole batch. The remediation is to not copy unbounded returndata from untrusted targets.

Before (Vulnerable)

function forward(address target, bytes calldata data) external {
    // The callee can return megabytes; copying it can burn all forwarded gas.
    (bool ok, bytes memory ret) = target.call(data);
    require(ok, string(ret));
}

After (Fixed — ignore returndata, or cap the copy in assembly)

function forward(address target, bytes calldata data) external {
    bool ok;
    assembly {
        // Copy zero bytes of returndata: success/failure is captured, the bomb is not.
        ok := call(gas(), target, 0, add(data, 0x20), data.length, 0, 0)
    }
    require(ok, "Call failed");
}

When you must read a return value, copy only a bounded prefix (for example the first 32 bytes for a bool/uint256) with returndatacopy, rather than the full returndatasize().

Common Mistakes

  • Using try/catch around an external call and letting the compiler-generated returndata copy absorb the bomb.
  • Copying returndatasize() bytes to build a revert reason, which re-exposes the same unbounded copy.
  • Assuming a low gas stipend protects you; the quadratic memory cost can exceed even large gas budgets.

References