Opcode Summary

PropertyValue
Opcode0x3E
MnemonicRETURNDATACOPY
Gas3 + 3 × ceil(len / 32) + mem_expansion
Stack InputdstOst, ost, len
Stack Output(none)
BehaviorCopies len bytes from the return data buffer (populated by the most recent external call) starting at offset ost into memory at position dstOst. Unlike CALLDATACOPY, reading past the return data boundary causes the current call to REVERT (out-of-gas exception) rather than zero-padding. Introduced in the Byzantium hard fork via EIP-211.

Threat Surface

RETURNDATACOPY occupies a uniquely dangerous position among the EVM’s memory-copy opcodes because of a critical asymmetry: while CALLDATACOPY silently zero-pads out-of-bounds reads, RETURNDATACOPY reverts. This single design difference — intentional per EIP-211 to prevent contracts from silently consuming garbage data from a previous call’s return buffer — is the root cause of an entire class of vulnerabilities.

The threat surface centers on three properties:

  1. The revert-on-OOB asymmetry is a bug magnet. Developers who internalize CALLDATACOPY’s zero-padding behavior frequently assume RETURNDATACOPY works the same way. It does not. Any code that reads past RETURNDATASIZE — even by a single byte, even with len = 0 if ost > RETURNDATASIZE — triggers an immediate revert. This catches Solidity’s high-level ABI decoding, hand-rolled assembly parsers, and optimizer assumptions alike. The Solidity compiler itself had a bug (issue #13039) where the optimizer incorrectly removed RETURNDATACOPY calls that should have reverted.

  2. The return data buffer is attacker-controlled. When a contract makes an external call to an untrusted address, the callee fully controls the return data buffer’s contents and size. A malicious callee can return or revert with megabytes of data. Solidity automatically generates RETURNDATACOPY to copy this data into the caller’s memory, and memory expansion cost is quadratic. This creates the “return bomb” attack: the callee spends 63/64 of forwarded gas producing massive return data, and the caller’s remaining 1/64 gas is insufficient to pay for the memory expansion triggered by the automatic RETURNDATACOPY, causing the entire transaction to fail.

  3. The return data buffer is ephemeral and context-sensitive. The buffer is overwritten by every external call (including calls that revert), reset after STATICCALL/DELEGATECALL, and empty before any call has been made. Code that reads the return data buffer after an intervening call — even a seemingly innocuous one like a balance check — silently gets the wrong call’s data, or reverts if the new buffer is shorter than expected.


Smart Contract Threats

T1: Out-of-Bounds Revert Catching Bugs (High)

RETURNDATACOPY reverts if ost + len > RETURNDATASIZE. Unlike CALLDATACOPY’s zero-padding, this means any mismatch between expected and actual return data length causes a hard revert. This creates several vulnerability patterns:

  • ABI decoding reverts on non-standard returns. Solidity’s abi.decode expects return data to be ABI-encoded with specific length. When a call target returns fewer bytes than expected (e.g., a non-standard ERC-20 that returns nothing on transfer()), the compiler-generated RETURNDATACOPY tries to read past the buffer and reverts. This is the root cause of the well-known “USDT can’t be transferred” class of bugs — IERC20(usdt).transfer() reverts because USDT doesn’t return a bool.

  • Unchecked return data length after low-level calls. Assembly code that calls returndatacopy(ptr, 0, 32) to read a single return word will revert if the callee returned nothing or returned fewer than 32 bytes. The developer assumed at least 32 bytes of return data, but the callee is under no obligation to return anything.

  • Conditional revert paths. If a callee reverts with a short custom error (e.g., 4-byte selector only) and the caller’s catch block tries to decode a longer error message, the RETURNDATACOPY for the decode reverts again, masking the original error with a secondary OOG revert.

Why it matters: The zero-pad vs. revert asymmetry between CALLDATACOPY and RETURNDATACOPY is one of the most common sources of integration bugs in DeFi, particularly when interacting with tokens that deviate from the ERC-20 return value convention.

T2: Return Data Buffer from Unexpected Call (High)

The return data buffer is a single global register that is overwritten by every external call, including calls embedded inside library functions or compiler-generated code. This leads to stale-buffer vulnerabilities:

  • Intervening calls overwrite the buffer. A contract calls target A, intending to read A’s return data, but an intermediate operation (a transfer, a log via a precompile, or even a Solidity require with a custom error string in some edge cases) makes another external call. The buffer now contains the second call’s return data, and RETURNDATACOPY reads the wrong data.

  • DELEGATECALL resets the buffer. After a DELEGATECALL, the return data buffer reflects the delegatecall target’s return, not the previous CALL’s. Proxy patterns that mix direct calls and delegatecalls can inadvertently read the wrong buffer.

  • Revert data replaces success data. If a contract calls A (which succeeds and returns data), then calls B (which reverts), the return data buffer now contains B’s revert reason. If the catch block naively reads “the last call’s return data,” it gets B’s error instead of A’s result.

Why it matters: Return data buffer confusion leads to silent data corruption — the contract proceeds with incorrect values rather than reverting, potentially causing incorrect token balances, wrong oracle prices, or broken state machine transitions.

T3: Memory Expansion Gas Griefing (High)

RETURNDATACOPY writes to memory, and EVM memory expansion cost is quadratic:

memory_cost = (words^2 / 512) + (3 * words)

Solidity generates RETURNDATACOPY after every external call to copy the return data into a memory buffer for ABI decoding. If the callee returns a massive payload, the RETURNDATACOPY triggers proportionally massive memory expansion in the caller’s execution context. The caller pays the quadratic gas cost, not the callee.

Vulnerable pattern: Any contract that makes an external call to an untrusted address and decodes the return value:

// Solidity automatically copies ALL return data into memory
(bool success, bytes memory data) = untrustedTarget.call(payload);
// 'data' now contains the full return -- if the target returned 1 MB,
// the memory expansion cost is ~500M gas, likely exceeding the block gas limit

Even high-level calls like IERC20(token).balanceOf(user) are vulnerable if token is an attacker-controlled address.

T4: Return Bomb Attacks (Critical)

The return bomb is a refined gas griefing attack that exploits the 63/64 gas forwarding rule combined with RETURNDATACOPY’s memory expansion cost:

  1. Contract A calls untrusted contract B, forwarding 63/64 of available gas.
  2. B executes revert(0, HUGE_NUMBER), spending nearly all forwarded gas on building a massive return data buffer (B’s memory expansion is paid from the forwarded gas).
  3. The external call returns to A. A has only 1/64 of its original gas remaining.
  4. Solidity’s generated code executes RETURNDATACOPY to copy B’s revert data into A’s memory.
  5. The memory expansion in A’s context for the massive return data requires far more than 1/64 of the original gas.
  6. A reverts with out-of-gas.

The attack is devastating because it circumvents the 63/64 gas rule’s safety guarantee. The rule is supposed to ensure that the caller always retains enough gas to continue execution after a subcall. But the automatic RETURNDATACOPY consumes the reserved 1/64 on memory expansion, leaving nothing for the caller’s remaining logic.

Why it matters: Return bombs can block critical protocol operations — liquidations, unstaking, governance execution — by making any transaction that touches the malicious callee revert. If the callee address is configurable (e.g., a callback hook, a delegation target, an oracle address), an attacker who controls it can permanently DoS the protocol.

T5: ABI Decoding of Malformed Return Data (Medium)

Solidity’s ABI decoder trusts return data structure implicitly. When return data is present but malformed, several issues arise:

  • Excess data is silently accepted. abi.decode does not revert when return data contains more bytes than the expected type requires. Extra bytes are ignored, which can mask encoding errors or allow unexpected data to pass unnoticed.

  • Dynamic type pointer manipulation. If a callee returns malformed ABI-encoded data with manipulated offset pointers, the decoder may read from unexpected memory regions. While bounds checking prevents reading outside the return data, the decoded values can be semantically invalid.

  • Empty return from non-reverting call. Some contracts (especially older tokens) return no data on success. Solidity >= 0.8.0 handles this with returndatasize checks, but hand-rolled assembly decoders that assume a minimum return size will revert via RETURNDATACOPY OOB.

Why it matters: Malformed return data is the attack surface for any protocol that integrates with arbitrary external contracts. The caller must defensively validate both the length and structure of return data before decoding.


Protocol-Level Threats

P1: Quadratic Memory Cost as DoS Vector (Medium)

At the protocol level, RETURNDATACOPY’s memory expansion cost creates an asymmetry: a callee can force the caller to spend quadratically more gas than the callee invested. In contexts where gas is subsidized — meta-transactions, relayers, account abstraction bundlers — the party paying for gas is not the party choosing the external call target. A malicious callee returning large data can drain the relayer’s gas budget across multiple transactions, degrading service availability.

P2: Client Implementation Divergence — Revert Semantics (Low)

EIP-211 specifies that RETURNDATACOPY must cause an “exceptional halt” when ost + len > RETURNDATASIZE or when the addition overflows. The exact type of exceptional halt (out-of-gas vs. invalid opcode behavior) must be consistent across clients. While no consensus bug has been attributed to RETURNDATACOPY, the revert-on-OOB semantics are more complex to implement correctly than CALLDATACOPY’s zero-padding, particularly in zkEVM circuits where conditional reverts must be proved.

P3: Solidity Compiler’s Automatic RETURNDATACOPY (Medium)

The Solidity compiler generates RETURNDATACOPY after every external call, even when the return value is unused. This was the subject of multiple Solidity issues (#12306, #13352) and creates a protocol-level concern: contracts compiled with current Solidity versions have a larger attack surface than necessary because every external call is a potential return bomb vector. The compiler’s optimizer also had a bug (#13039) where it incorrectly removed RETURNDATACOPY instructions that should have reverted, violating EIP-211 semantics and potentially causing contracts to silently accept invalid return data.


Edge Cases

Edge CaseBehaviorSecurity Implication
ost + len > RETURNDATASIZEREVERTS (out-of-gas exception)Critical difference from CALLDATACOPY (which zero-pads). Causes integration failures with non-standard return values (e.g., USDT).
len = 0 with ost <= RETURNDATASIZENo-op; no memory expansion, no revertSafe, but ost > RETURNDATASIZE with len = 0 still reverts per EIP-211 specification
len = 0 with ost > RETURNDATASIZEREVERTSSurprising edge case: even zero-length copies revert if the offset is out of bounds. The Solidity optimizer had a bug removing this case.
Before any external callReturn data buffer is empty (RETURNDATASIZE = 0)Any RETURNDATACOPY with len > 0 reverts. Code that assumes a prior call has been made will fail.
After a REVERTing callBuffer contains the revert data, not success dataCallers reading return data in a catch block get the revert reason; RETURNDATASIZE reflects the revert payload length
After STATICCALLBuffer contains the staticcall’s return dataSame as CALL; no special behavior, but state-changing reverts inside staticcall produce different revert data
After DELEGATECALLBuffer contains the delegatecall target’s return dataThe delegate’s return overwrites the buffer; intervening delegatecalls silently replace the expected return data
After a call to a non-existent accountBuffer is empty (RETURNDATASIZE = 0)EOAs and empty accounts return no data; RETURNDATACOPY with len > 0 reverts
dstOst + len causes memory expansion beyond gas limitTransaction reverts with out-of-gasThe return bomb vector: quadratic memory cost on attacker-controlled return data size
Overflow in ost + lenREVERTS (specified by EIP-211)Arithmetic overflow in bounds check is explicitly handled; cannot be used to bypass the OOB check

Real-World Exploits

Exploit 1: Return Data Bombing RAI’s Liquidation Engine — Protocol Insolvency Risk (September 2023)

Root cause: Unbounded RETURNDATACOPY in Reflexer Finance’s (RAI) LiquidationEngine catch clause, combined with the 63/64 gas forwarding rule, allowed a malicious Safe Saviour to create unliquidatable positions.

Details: RAI’s Safe Saviour feature allows external contracts to rescue underwater positions during liquidation. The LiquidationEngine calls the saviour contract and catches any revert in a try/catch block. The catch clause receives the revert data as bytes memory, which triggers Solidity’s automatic RETURNDATACOPY.

An attacker deploys a malicious saviour contract that, when called during liquidation, executes revert(0, HUGE_NUMBER). The revert consumes 63/64 of the gas forwarded to the saviour. When execution returns to the LiquidationEngine’s catch block, Solidity’s generated RETURNDATACOPY attempts to copy the massive revert payload into memory. The memory expansion cost exceeds the remaining 1/64 gas, causing the entire liquidation transaction to revert with out-of-gas.

Since the attacker’s saviour contract deterministically returns massive data on every call, the position becomes permanently unliquidatable. If enough positions become unliquidatable, the protocol becomes insolvent.

RETURNDATACOPY’s role: The attack is possible only because Solidity’s catch clause copies the full revert data into memory via RETURNDATACOPY. If the catch clause bounded the copy (or Solidity didn’t automatically copy revert data), the attack would fail.

Impact: Critical severity — protocol insolvency risk through permanent liquidation DoS. The bug remained unpatched as of early 2026.

References:


Exploit 2: EigenLayer DelegationManager — Delegator Griefing via Return Bomb (2023-2024)

Root cause: The EigenLayer DelegationManager called operator-specified callback contracts without bounding return data size, allowing operators to deploy malicious callbacks that prevented delegators from undelegating.

Details: EigenLayer’s delegation system allows operators to specify callback contracts that are invoked during delegation and undelegation flows. The _delegationWithdrawnHook function called these operator-controlled contracts and processed their return data through standard Solidity mechanisms, which automatically copy all return data via RETURNDATACOPY.

A malicious operator could deploy a callback that returns (or reverts with) an extremely large payload. When a delegator attempted to undelegate, the callback’s massive return data triggered quadratic memory expansion in the DelegationManager’s call frame, consuming all remaining gas and reverting the undelegation transaction. Delegators’ funds were effectively locked.

RETURNDATACOPY’s role: The automatic RETURNDATACOPY generated by Solidity to process the callback’s return data was the gas sink. The callback’s return data size was unbounded, and memory expansion cost exceeded the caller’s available gas.

Impact: High severity — delegator fund lockup. EigenLayer fixed this by implementing bounded return data copying using Yul assembly (limiting return data to 32 bytes), completely eliminating the attack vector.

References:


Exploit 3: Solidity Optimizer — Incorrect RETURNDATACOPY Removal (May 2022)

Root cause: The Solidity compiler’s UnusedStoreEliminator optimization pass incorrectly removed RETURNDATACOPY instructions that should have caused out-of-gas exceptions, violating EIP-211 semantics.

Details: The optimizer treated RETURNDATACOPY similarly to other memory-write opcodes and removed it when the written memory was never read. However, RETURNDATACOPY has a side effect that other memory writes do not: it reverts if the read would go out of bounds. The optimizer removed these “dead” stores, silently converting what should have been a revert into successful execution.

Two specific cases were identified through fuzzing:

  • returndatacopy(0, 0, 1) after a call returning 0 bytes — should revert, but optimized code continued.
  • returndatacopy(0, 1, 0) after a call returning 0 bytes — should revert (offset past buffer), but optimized code continued.

RETURNDATACOPY’s role: The bug is entirely about RETURNDATACOPY’s unique revert-on-OOB semantics. CALLDATACOPY, CODECOPY, and EXTCODECOPY all zero-pad out-of-bounds reads and have no reverting side effect, so removing them as dead stores is safe. The optimizer incorrectly applied the same logic to RETURNDATACOPY.

Impact: Medium severity — contracts compiled with affected optimizer versions could silently proceed past what should have been a boundary check. The fix (PR #13040) added special handling to preserve potentially reverting RETURNDATACOPY instructions.

References:


Exploit 4: Non-Standard ERC-20 Return Values — USDT and the “Missing Return” Class (2017–present)

Root cause: Tether (USDT), BNB, and other high-value ERC-20 tokens do not return a bool from transfer() and approve(), violating the ERC-20 specification. Solidity’s generated RETURNDATACOPY to decode the expected bool reads past the empty return data buffer and reverts.

Details: The ERC-20 standard specifies that transfer() returns bool. Solidity’s ABI decoder generates code that calls RETURNDATASIZE to check how much data was returned, then RETURNDATACOPY to copy it for decoding. When a token like USDT returns nothing, RETURNDATASIZE is 0, and the decoder’s attempt to copy 32 bytes reverts.

This is not a single exploit but a pervasive integration failure class. Any protocol that calls IERC20(token).transfer(to, amount) against a non-compliant token will revert. OpenZeppelin’s SafeERC20.safeTransfer() was created specifically to handle this case — it checks RETURNDATASIZE and only decodes if data is present.

RETURNDATACOPY’s role: The revert is triggered by RETURNDATACOPY attempting to read 32 bytes from an empty return data buffer. If RETURNDATACOPY zero-padded like CALLDATACOPY, the decoder would read false (all zeros) but would not revert, leading to a different (arguably worse) failure mode.

Impact: Hundreds of millions of dollars in protocol integrations required workarounds. USDT alone has a market cap exceeding $100B, and every protocol integrating it must use SafeERC20 or equivalent wrappers.

References:


Attack Scenarios

Scenario A: Return Bomb Blocking Liquidations

// Malicious "Safe Saviour" that blocks all liquidations via return bomb
contract MaliciousSaviour {
    fallback() external {
        assembly {
            // Allocate a massive revert payload using 63/64 of forwarded gas.
            // The caller's remaining 1/64 gas cannot pay for memory expansion
            // when Solidity's catch clause copies this revert data.
            revert(0, 0x100000) // 1 MB revert payload
        }
    }
}
 
contract LiquidationEngine {
    function liquidate(address safe, address saviour) external {
        // 63/64 gas forwarded to saviour
        try ISaviour(saviour).save(safe) {
            // Saviour saved the position, skip liquidation
            return;
        } catch (bytes memory reason) {
            // VULNERABLE: Solidity copies ALL revert data into 'reason'
            // via RETURNDATACOPY. Memory expansion for 1 MB costs ~500M gas.
            // Remaining 1/64 gas is far insufficient -> entire tx reverts OOG
        }
        // Liquidation logic never reached
        _performLiquidation(safe);
    }
}

Scenario B: Return Bomb via Callback Hook

contract StakingPool {
    mapping(address => uint256) public stakes;
    mapping(address => address) public callbacks;
 
    function unstake() external {
        uint256 amount = stakes[msg.sender];
        require(amount > 0);
        stakes[msg.sender] = 0;
 
        // Call user's callback -- vulnerable to return bomb
        address hook = callbacks[msg.sender];
        if (hook != address(0)) {
            // Solidity auto-copies return data via RETURNDATACOPY
            (bool ok, bytes memory data) = hook.call(
                abi.encodeWithSignature("onUnstake(uint256)", amount)
            );
        }
 
        // Transfer may never execute if return bomb consumed all gas
        payable(msg.sender).transfer(amount);
    }
}
 
// Attacker sets their callback to a return bomb contract,
// making unstake() revert for them -> funds locked permanently
contract ReturnBomb {
    fallback() external {
        assembly {
            revert(0, 0x100000)
        }
    }
}

Scenario C: OOB Revert on Non-Standard Token

contract VulnerableVault {
    // This function reverts when called with USDT because
    // USDT's transfer() returns nothing (no bool).
    function withdraw(IERC20 token, uint256 amount) external {
        // Solidity generates: CALL + RETURNDATASIZE check + RETURNDATACOPY
        // USDT returns 0 bytes -> RETURNDATACOPY(ptr, 0, 32) reverts OOB
        token.transfer(msg.sender, amount); // REVERTS for USDT!
    }
}
 
contract SafeVault {
    using SafeERC20 for IERC20;
 
    function withdraw(IERC20 token, uint256 amount) external {
        // SafeERC20 checks RETURNDATASIZE first:
        // - If 0 bytes returned: assumes success (for non-compliant tokens)
        // - If 32 bytes returned: decodes and checks the bool
        // - If other length: reverts
        token.safeTransfer(msg.sender, amount);
    }
}

Scenario D: Stale Return Data Buffer

contract DataConfusion {
    function riskyOperation(address tokenA, address tokenB) external {
        // Call tokenA -- return data buffer now holds tokenA's response
        uint256 balanceA;
        assembly {
            let success := call(gas(), tokenA, 0,
                /* calldata for balanceOf(this) */ 0x80, 0x24,
                0, 0)
            // Read tokenA's return data
            returndatacopy(0x00, 0, 0x20)
            balanceA := mload(0x00)
        }
 
        // Call tokenB -- return data buffer is NOW OVERWRITTEN
        assembly {
            let success := call(gas(), tokenB, 0,
                /* calldata for totalSupply() */ 0x80, 0x04,
                0, 0)
        }
 
        // BUG: developer intended to read more of tokenA's return data
        // but the buffer now contains tokenB's totalSupply() return
        uint256 tokenAExtra;
        assembly {
            // This reads tokenB's data, NOT tokenA's!
            // If tokenB returned less data than expected, this reverts OOB
            returndatacopy(0x00, 0x20, 0x20)
            tokenAExtra := mload(0x00)
        }
    }
}

Mitigations

ThreatMitigationImplementation
T1: OOB revert on non-standard returnsUse SafeERC20 for all token interactionsOpenZeppelin SafeERC20.safeTransfer() / safeApprove() checks RETURNDATASIZE before decoding
T1: Assembly OOB readsAlways check RETURNDATASIZE before RETURNDATACOPYif gt(returndatasize(), 31) { returndatacopy(ptr, 0, 32) } in assembly
T2: Stale return data bufferRead return data immediately after the call, before any other operationsNever assume the buffer persists across multiple external calls
T3: Memory expansion griefingBound return data copy size in assemblyUse returndatacopy(ptr, 0, min(returndatasize(), MAX_COPY)) pattern
T4: Return bomb attacksUse ExcessivelySafeCall or equivalent bounded-copy libraryNomad’s ExcessivelySafeCall limits bytes copied to memory via _maxCopy parameter
T4: Return bomb in catch blocksAvoid catch (bytes memory reason) with untrusted calleesUse catch { } (no data capture) or catch (bytes memory reason) with assembly-bounded copy
T4: Critical path protectionEnsure liquidation/unstaking paths cannot be DoS’dUse Yul assembly to make calls with bounded return data (EigenLayer pattern): limit to 32 bytes
T5: Malformed return dataValidate return data length matches expected ABI encodingCheck returndatasize() >= expectedMinLength before decoding
General: All return bomb vectorsCap return data at the call site using inline assemblylet success := call(gas(), target, 0, inPtr, inLen, 0, 0) then returndatacopy(outPtr, 0, min(returndatasize(), maxLen))

Compiler/EIP-Based Protections

  • Solidity SafeERC20 (OpenZeppelin): Industry-standard wrapper that checks RETURNDATASIZE before RETURNDATACOPY, handling non-compliant tokens and empty returns. Every DeFi protocol should use it for token interactions.
  • ExcessivelySafeCall (Nomad): Library providing excessivelySafeCall() with a _maxCopy parameter that bounds the RETURNDATACOPY size at the call site. Designed for bridges, relayers, and meta-transaction protocols.
  • Solidity PR #13040 (May 2022): Fixes the optimizer bug that incorrectly removed reverting RETURNDATACOPY instructions. All production contracts should compile with post-fix Solidity versions.
  • EIP-211 (Byzantium, 2017): The specification that introduced RETURNDATACOPY intentionally chose revert-on-OOB (instead of zero-padding) to prevent contracts from silently reading stale data from a previous call’s buffer. This was a deliberate safety tradeoff.
  • Solidity Issue #12306 / #13352 (open): Proposals to eliminate automatic RETURNDATACOPY generation when return data is unused. Until resolved, every external call in Solidity is a potential return bomb surface.

Severity Summary

Threat IDCategorySeverityLikelihoodReal-World Precedent
T1Smart ContractHighHighUSDT/non-standard ERC-20 integration failures (pervasive)
T2Smart ContractHighMediumStale buffer bugs in multi-call assembly patterns
T3Smart ContractHighMediumGas griefing via memory expansion (class of attacks)
T4Smart ContractCriticalHighRAI LiquidationEngine return bomb, EigenLayer DelegationManager griefing
T5Smart ContractMediumMediumABI decoding edge cases, Solidity compiler bug #13039
P1ProtocolMediumMediumRelayer gas drainage via return bombs
P2ProtocolLowLow
P3ProtocolMediumMediumSolidity optimizer bug; open compiler issues for unnecessary RETURNDATACOPY

OpcodeRelationship
RETURNDATASIZE (0x3D)Returns the size of the return data buffer; must be checked before RETURNDATACOPY to avoid OOB reverts. The two opcodes were introduced together in EIP-211.
CALLDATACOPY (0x37)Same memory-copy semantics but zero-pads out-of-bounds reads instead of reverting. This asymmetry is a primary source of RETURNDATACOPY-related bugs.
CALL (0xF1)Populates the return data buffer with the callee’s return data. The 63/64 gas forwarding rule combined with return bombs is the primary attack vector.
STATICCALL (0xFA)Read-only call that also populates the return data buffer. Same return bomb risk as CALL.
DELEGATECALL (0xF4)Overwrites the return data buffer with the delegate’s return; can cause stale-buffer confusion in proxy patterns.
CODECOPY (0x39)Memory-copy opcode that zero-pads OOB reads (like CALLDATACOPY); contrasts with RETURNDATACOPY’s revert behavior.
REVERT (0xFD)Populates the return data buffer with revert reason data. Return bombs typically use REVERT to fill the buffer with massive payloads.
EXTCODECOPY (0x3C)Copies external contract code to memory; zero-pads like CALLDATACOPY, not like RETURNDATACOPY.