Skip to content

fix(evm): fix multiple interpreter gas and call handling bugs#372

Open
starwarfan wants to merge 3 commits intoDTVMStack:mainfrom
starwarfan:fix-interp-gas-and-call-handling
Open

fix(evm): fix multiple interpreter gas and call handling bugs#372
starwarfan wants to merge 3 commits intoDTVMStack:mainfrom
starwarfan:fix-interp-gas-and-call-handling

Conversation

@starwarfan
Copy link
Contributor

This PR fixes six additional bugs found by evmone-fuzzer differential testing in interpreter mode, resolving the remaining ~440 crash files after PR #371.

Bug 1 — Stale static gas cost tables: The calculateGas() macros in opcode_handlers.cpp used static variables to cache the instruction metrics table pointer and gas cost. These were initialized once on the first call and never updated, returning stale gas costs when the EVM revision changed between fuzzer test cases in the individual instruction fallback path.

Bug 2 — uint256-to-uint64 truncation in CALL gas: CallHandler::doExecute() used static_cast<uint64_t>(Gas) to convert the intx::uint256 gas argument from the stack, silently truncating bits above bit 63. Large gas values (e.g., 0xFFFF...) became small values instead of being clamped to UINT64_MAX. Changed to use the existing uint256ToUint64() helper.

Bug 3 — SELFDESTRUCT not clearing return data: SelfDestructHandler::doExecute() did not clear ReturnData, leaving stale data from prior operations (e.g., STATICCALL) as the final output. The EVM spec requires SELFDESTRUCT to produce empty output.

Bug 4 — CREATE unconditionally calling get_balance: CreateHandler::doExecute() always called Frame->Host->get_balance(Frame->Msg.recipient) even when Value == 0. In the MockedHost, get_balance() calls record_account_access(), which implicitly warms the address for future access_account() calls. Evmone short-circuits with endowment != 0 &&, skipping the call when value is zero. This caused DTVM to skip 2600-gas EIP-2929 cold access charges that evmone correctly applied (32 gas_left mismatches, all differing by exactly 2600).

Bug 5 — SSTORE minimum gas check affected by gas chunk pre-charging: The gas chunk mechanism pre-charges the entire block's gas cost before executing any instructions. When a block contained SSTORE followed by expensive instructions (e.g., SELFDESTRUCT with 5000 gas), the SSTORE EIP-2200 minimum gas check (gas_left <= 2300) saw artificially low gas because future instructions' costs were already deducted. Added OP_SSTORE as a gas chunk terminator so blocks always end at SSTORE boundaries, matching evmone's per-instruction gas semantics (7 status mismatches).

Bug 6 — CREATE only setting return data on REVERT: CreateHandler::doExecute() only preserved the sub-call's output as ReturnData when the result status was EVMC_REVERT, clearing it for all other statuses (SUCCESS, FAILURE, etc.). Evmone always sets return_data from the call result regardless of status (matching EIP-211). This caused RETURNDATASIZE to return 0 instead of the actual output size after non-REVERT CREATE failures, corrupting subsequent stack values and producing wrong selfdestruct records (2 selfdestruct mismatches, 2 gas_left mismatches).

1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):

  • N
  • Y

2. What is the scope of this PR (e.g. component or file name):

src/evm/opcode_handlers.cpp, src/evm/evm_cache.cpp

3. Provide a description of the PR(e.g. more details, effects, motivations or doc link):

  • Affects user behaviors
  • Contains CI/CD configuration changes
  • Contains documentation changes
  • Contains experimental features
  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Other

This PR depends on and includes the commit from PR #371 (SPP gas cost shifting fix and opcode validity check). Together they resolve all ~13,150 interpreter-mode fuzz crashes found by evmone-fuzzer differential testing.

4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):

  • N
  • Y

5. Provide a description of the tests:

Verified with evmone-fuzzer differential testing in interpreter mode:

  • All 445 previously failing crash files now pass (0 failures)
  • Sampled 200 of 12,705 previously fixed crash files — all pass
  • No regressions observed

Made with Cursor

… in interpreter fallback path

The SPP (Safe Prepaid Points) optimization shifted gas costs between basic
blocks to reduce metering points. When a predecessor block's gas chunk could
not be paid for (insufficient gas), the interpreter fell back to per-instruction
execution for that block. However, successor blocks whose costs had been shifted
to the predecessor retained a metered cost of 0, allowing them to execute their
instructions without any gas charge.

Additionally, the individual instruction dispatch path (used when the gas chunk
condition fails) did not check opcode validity against the current EVM revision.
Since undefined opcodes have gas_cost=0 in the EVMC metrics table, they passed
the gas check and executed, producing incorrect results for revision-sensitive
opcodes like PUSH0 (Shanghai+), SELFBALANCE (Istanbul+), BASEFEE (London+),
and RETURNDATASIZE (Byzantium+).

Fix 1: Use original basic-block gas costs instead of SPP-shifted costs for the
gas chunk cache. This preserves the per-block gas deduction optimization while
eliminating the inter-block cost transfer that caused the correctness issue.

Fix 2: Add a NamesTable validity check in the individual instruction path,
matching the check already present in the gas chunk fast path. Undefined opcodes
now correctly trigger EVMC_UNDEFINED_INSTRUCTION.

Fixes ~12,800 fuzz crashes in interpreter mode (status mismatch: success vs
failure) found by evmone-fuzzer differential testing.

Made-with: Cursor
Fix seven bugs found by evmone-fuzzer differential testing:

1. Remove static from calculateGas macros - static variables cached the
   metrics table from the first call, returning stale gas costs when the
   EVM revision changed between fuzzer test cases.

2. Fix uint256-to-uint64 truncation in CALL gas argument - use
   uint256ToUint64 (clamps to UINT64_MAX) instead of static_cast which
   silently truncated high bits, producing incorrect sub-call gas values.

3. Clear return data on SELFDESTRUCT - SELFDESTRUCT must produce empty
   output, but DTVM was leaving stale return data from prior operations.

4. Short-circuit CREATE balance check when value is zero - the
   unconditional get_balance call recorded an account access in the mock
   host, incorrectly warming msg.recipient and causing EIP-2929 cold
   access charges to be skipped for later instructions.

5. Add SSTORE as gas chunk terminator - the gas chunk mechanism
   pre-charges the entire block cost before executing instructions.
   SSTORE's EIP-2200 minimum gas check (gas_left > 2300) saw
   artificially low gas because future instructions' costs (e.g.
   SELFDESTRUCT's 5000) were already deducted.

6. Always set return data from CREATE call result - DTVM only preserved
   return data on REVERT, but evmone (and the EVM spec) always sets
   RETURNDATASIZE from the sub-call result regardless of status. This
   caused RETURNDATASIZE mismatches after failed CREATE calls, leading
   to wrong stack values and selfdestruct record differences.

Fixes remaining ~440 fuzz crashes in interpreter mode found by
evmone-fuzzer differential testing (gas_left, status, output, and
selfdestruct mismatches).

Made-with: Cursor
@github-actions
Copy link

github-actions bot commented Feb 28, 2026

⚡ Performance Regression Check Results

✅ Performance Check Passed (interpreter)

Performance Benchmark Results (threshold: 20%)

Benchmark Baseline (us) Current (us) Change Status
total/main/blake2b_huff/8415nulls 1.65 2.17 +31.1% PASS
total/main/blake2b_huff/empty 0.08 0.19 +140.2% PASS
total/main/blake2b_shifts/8415nulls 15.32 17.90 +16.8% PASS
total/main/sha1_divs/5311 7.71 7.45 -3.4% PASS
total/main/sha1_divs/empty 0.10 0.10 -2.4% PASS
total/main/sha1_shifts/5311 5.51 5.52 +0.1% PASS
total/main/sha1_shifts/empty 0.07 0.07 +0.2% PASS
total/main/snailtracer/benchmark 62.04 68.07 +9.7% PASS
total/main/structarray_alloc/nfts_rank 1.14 1.32 +14.9% PASS
total/main/swap_math/insufficient_liquidity 0.01 0.01 +4.1% PASS
total/main/swap_math/received 0.01 0.01 +2.6% PASS
total/main/swap_math/spent 0.01 0.01 +1.8% PASS
total/main/weierstrudel/1 0.29 0.32 +10.2% PASS
total/main/weierstrudel/15 2.72 2.92 +7.2% PASS
total/micro/JUMPDEST_n0/empty 1.64 1.97 +20.1% PASS
total/micro/jump_around/empty 0.14 0.14 +1.8% PASS
total/micro/loop_with_many_jumpdests/empty 24.92 29.92 +20.1% REGRESSED
total/micro/memory_grow_mload/by1 0.13 0.14 +8.5% PASS
total/micro/memory_grow_mload/by16 0.19 0.19 +0.0% PASS
total/micro/memory_grow_mload/by32 0.22 0.23 +4.5% PASS
total/micro/memory_grow_mload/nogrow 0.13 0.14 +4.8% PASS
total/micro/memory_grow_mstore/by1 0.16 0.19 +19.1% PASS
total/micro/memory_grow_mstore/by16 0.19 0.23 +22.3% PASS
total/micro/memory_grow_mstore/by32 0.19 0.24 +23.7% PASS
total/micro/memory_grow_mstore/nogrow 0.16 0.18 +15.9% PASS
total/micro/signextend/one 0.27 0.30 +11.5% PASS
total/micro/signextend/zero 0.27 0.30 +11.5% PASS
total/synth/ADD/b0 2.25 2.62 +16.3% PASS
total/synth/ADD/b1 2.47 2.44 -1.2% PASS
total/synth/ADDRESS/a0 4.34 4.40 +1.6% PASS
total/synth/ADDRESS/a1 4.58 4.67 +2.0% PASS
total/synth/AND/b0 1.97 2.43 +23.0% PASS
total/synth/AND/b1 2.02 2.31 +14.3% PASS
total/synth/BYTE/b0 6.11 6.15 +0.7% PASS
total/synth/BYTE/b1 5.08 4.88 -3.8% PASS
total/synth/CALLDATASIZE/a0 2.47 2.58 +4.5% PASS
total/synth/CALLDATASIZE/a1 2.96 3.09 +4.4% PASS
total/synth/CALLER/a0 4.31 4.42 +2.5% PASS
total/synth/CALLER/a1 4.92 4.74 -3.5% PASS
total/synth/CALLVALUE/a0 1.97 2.17 +10.5% PASS
total/synth/CALLVALUE/a1 1.98 2.26 +14.1% PASS
total/synth/CODESIZE/a0 2.45 3.02 +23.2% PASS
total/synth/CODESIZE/a1 2.97 3.36 +13.3% PASS
total/synth/DUP1/d0 1.48 1.23 -16.4% PASS
total/synth/DUP1/d1 1.57 1.24 -21.3% PASS
total/synth/DUP10/d0 1.49 1.28 -13.7% PASS
total/synth/DUP10/d1 1.56 1.25 -19.8% PASS
total/synth/DUP11/d0 1.49 1.24 -16.3% PASS
total/synth/DUP11/d1 1.50 1.25 -16.5% PASS
total/synth/DUP12/d0 1.49 1.25 -16.2% PASS
total/synth/DUP12/d1 1.57 1.25 -20.4% PASS
total/synth/DUP13/d0 1.49 1.25 -16.3% PASS
total/synth/DUP13/d1 1.55 1.25 -19.6% PASS
total/synth/DUP14/d0 1.43 1.25 -12.8% PASS
total/synth/DUP14/d1 1.55 1.25 -19.7% PASS
total/synth/DUP15/d0 1.50 1.32 -12.1% PASS
total/synth/DUP15/d1 1.53 1.25 -18.5% PASS
total/synth/DUP16/d0 1.41 1.25 -11.6% PASS
total/synth/DUP16/d1 1.50 1.25 -16.6% PASS
total/synth/DUP2/d0 1.48 1.24 -16.2% PASS
total/synth/DUP2/d1 1.58 1.25 -20.6% PASS
total/synth/DUP3/d0 1.48 1.24 -16.1% PASS
total/synth/DUP3/d1 1.49 1.32 -11.7% PASS
total/synth/DUP4/d0 1.48 1.24 -16.1% PASS
total/synth/DUP4/d1 1.52 1.25 -17.7% PASS
total/synth/DUP5/d0 1.48 1.24 -16.2% PASS
total/synth/DUP5/d1 1.56 1.25 -19.9% PASS
total/synth/DUP6/d0 1.49 1.24 -16.2% PASS
total/synth/DUP6/d1 1.54 1.25 -18.9% PASS
total/synth/DUP7/d0 1.49 1.25 -16.0% PASS
total/synth/DUP7/d1 1.54 1.25 -19.0% PASS
total/synth/DUP8/d0 1.49 1.24 -16.3% PASS
total/synth/DUP8/d1 1.49 1.26 -15.8% PASS
total/synth/DUP9/d0 1.49 1.25 -16.1% PASS
total/synth/DUP9/d1 1.49 1.25 -16.1% PASS
total/synth/EQ/b0 4.80 4.81 +0.1% PASS
total/synth/EQ/b1 4.99 4.91 -1.6% PASS
total/synth/GAS/a0 2.69 2.74 +1.9% PASS
total/synth/GAS/a1 3.36 3.11 -7.3% PASS
total/synth/GT/b0 4.55 4.57 +0.3% PASS
total/synth/GT/b1 4.66 4.72 +1.3% PASS
total/synth/ISZERO/u0 6.99 7.65 +9.5% PASS
total/synth/JUMPDEST/n0 1.64 1.97 +20.0% PASS
total/synth/LT/b0 4.56 4.57 +0.1% PASS
total/synth/LT/b1 4.69 4.79 +2.2% PASS
total/synth/MSIZE/a0 3.84 4.05 +5.4% PASS
total/synth/MSIZE/a1 4.33 4.13 -4.6% PASS
total/synth/MUL/b0 4.05 4.22 +4.3% PASS
total/synth/MUL/b1 4.38 4.29 -2.0% PASS
total/synth/NOT/u0 1.65 3.51 +113.3% PASS
total/synth/OR/b0 1.89 2.50 +32.4% PASS
total/synth/OR/b1 2.23 2.32 +3.9% PASS
total/synth/PC/a0 2.48 3.08 +24.3% PASS
total/synth/PC/a1 2.97 3.20 +8.0% PASS
total/synth/PUSH1/p0 1.74 1.25 -28.1% PASS
total/synth/PUSH1/p1 1.50 1.22 -18.4% PASS
total/synth/PUSH10/p0 1.79 1.28 -28.5% PASS
total/synth/PUSH10/p1 1.56 1.29 -17.3% PASS
total/synth/PUSH11/p0 1.77 1.28 -27.6% PASS
total/synth/PUSH11/p1 1.55 1.26 -18.2% PASS
total/synth/PUSH12/p0 1.77 1.28 -27.7% PASS
total/synth/PUSH12/p1 1.55 1.27 -18.5% PASS
total/synth/PUSH13/p0 1.77 1.28 -27.5% PASS
total/synth/PUSH13/p1 1.54 1.28 -17.1% PASS
total/synth/PUSH14/p0 1.80 1.33 -26.1% PASS
total/synth/PUSH14/p1 1.53 1.33 -13.2% PASS
total/synth/PUSH15/p0 1.78 1.29 -27.3% PASS
total/synth/PUSH15/p1 1.55 1.36 -12.4% PASS
total/synth/PUSH16/p0 1.82 1.29 -28.9% PASS
total/synth/PUSH16/p1 1.53 1.28 -16.8% PASS
total/synth/PUSH17/p0 1.78 1.29 -27.4% PASS
total/synth/PUSH17/p1 1.56 1.28 -17.6% PASS
total/synth/PUSH18/p0 1.80 1.30 -27.5% PASS
total/synth/PUSH18/p1 1.57 1.53 -2.2% PASS
total/synth/PUSH19/p0 1.79 1.30 -27.1% PASS
total/synth/PUSH19/p1 1.58 1.34 -15.3% PASS
total/synth/PUSH2/p0 1.74 1.26 -27.7% PASS
total/synth/PUSH2/p1 1.52 1.24 -18.6% PASS
total/synth/PUSH20/p0 1.80 1.30 -27.6% PASS
total/synth/PUSH20/p1 1.57 1.31 -16.5% PASS
total/synth/PUSH21/p0 1.80 1.31 -27.3% PASS
total/synth/PUSH21/p1 1.57 1.29 -17.7% PASS
total/synth/PUSH22/p0 1.79 1.31 -27.0% PASS
total/synth/PUSH22/p1 1.58 1.29 -18.0% PASS
total/synth/PUSH23/p0 1.80 1.31 -27.1% PASS
total/synth/PUSH23/p1 1.56 1.55 -0.5% PASS
total/synth/PUSH24/p0 1.80 1.31 -27.1% PASS
total/synth/PUSH24/p1 1.61 1.37 -15.3% PASS
total/synth/PUSH25/p0 1.82 1.32 -27.3% PASS
total/synth/PUSH25/p1 1.57 1.37 -12.6% PASS
total/synth/PUSH26/p0 1.80 1.33 -26.1% PASS
total/synth/PUSH26/p1 1.56 1.37 -12.3% PASS
total/synth/PUSH27/p0 1.81 1.32 -27.0% PASS
total/synth/PUSH27/p1 1.58 1.37 -13.1% PASS
total/synth/PUSH28/p0 1.81 1.32 -26.9% PASS
total/synth/PUSH28/p1 1.59 1.32 -17.4% PASS
total/synth/PUSH29/p0 1.81 1.33 -26.7% PASS
total/synth/PUSH29/p1 1.60 1.31 -18.1% PASS
total/synth/PUSH3/p0 1.75 1.26 -27.9% PASS
total/synth/PUSH3/p1 1.53 1.25 -18.6% PASS
total/synth/PUSH30/p0 1.84 1.39 -24.6% PASS
total/synth/PUSH30/p1 1.62 1.39 -14.4% PASS
total/synth/PUSH31/p0 1.81 1.34 -26.2% PASS
total/synth/PUSH31/p1 1.59 1.45 -8.7% PASS
total/synth/PUSH32/p0 1.82 1.34 -26.7% PASS
total/synth/PUSH32/p1 1.57 1.34 -14.7% PASS
total/synth/PUSH4/p0 1.75 1.27 -27.5% PASS
total/synth/PUSH4/p1 1.50 1.24 -17.3% PASS
total/synth/PUSH5/p0 1.76 1.26 -28.2% PASS
total/synth/PUSH5/p1 1.53 1.24 -19.0% PASS
total/synth/PUSH6/p0 1.76 1.28 -27.3% PASS
total/synth/PUSH6/p1 1.51 1.24 -18.1% PASS
total/synth/PUSH7/p0 1.76 1.27 -27.8% PASS
total/synth/PUSH7/p1 1.54 1.32 -14.3% PASS
total/synth/PUSH8/p0 1.78 1.28 -27.9% PASS
total/synth/PUSH8/p1 1.56 1.31 -15.9% PASS
total/synth/PUSH9/p0 1.77 1.27 -28.2% PASS
total/synth/PUSH9/p1 1.53 1.26 -18.0% PASS
total/synth/RETURNDATASIZE/a0 2.47 2.82 +14.2% PASS
total/synth/RETURNDATASIZE/a1 3.00 3.34 +11.4% PASS
total/synth/SAR/b0 3.42 3.55 +3.6% PASS
total/synth/SAR/b1 4.04 3.95 -2.2% PASS
total/synth/SGT/b0 4.74 4.85 +2.2% PASS
total/synth/SGT/b1 4.57 4.77 +4.2% PASS
total/synth/SHL/b0 3.67 3.96 +7.8% PASS
total/synth/SHL/b1 2.02 2.67 +31.7% PASS
total/synth/SHR/b0 2.99 3.10 +3.7% PASS
total/synth/SHR/b1 2.41 2.62 +9.0% PASS
total/synth/SIGNEXTEND/b0 2.06 2.46 +19.4% PASS
total/synth/SIGNEXTEND/b1 2.44 2.69 +10.3% PASS
total/synth/SLT/b0 4.74 4.82 +1.8% PASS
total/synth/SLT/b1 4.62 4.77 +3.2% PASS
total/synth/SUB/b0 2.25 2.63 +16.7% PASS
total/synth/SUB/b1 2.24 2.42 +8.3% PASS
total/synth/SWAP1/s0 2.30 1.81 -21.1% PASS
total/synth/SWAP10/s0 2.31 1.82 -21.1% PASS
total/synth/SWAP11/s0 2.32 1.84 -20.5% PASS
total/synth/SWAP12/s0 2.32 1.83 -21.2% PASS
total/synth/SWAP13/s0 2.32 1.83 -21.0% PASS
total/synth/SWAP14/s0 2.32 1.83 -21.0% PASS
total/synth/SWAP15/s0 1.94 1.83 -5.8% PASS
total/synth/SWAP16/s0 2.32 1.77 -23.7% PASS
total/synth/SWAP2/s0 2.24 1.83 -18.5% PASS
total/synth/SWAP3/s0 2.31 1.80 -22.1% PASS
total/synth/SWAP4/s0 2.31 1.82 -21.3% PASS
total/synth/SWAP5/s0 2.15 1.82 -15.3% PASS
total/synth/SWAP6/s0 2.31 1.82 -21.1% PASS
total/synth/SWAP7/s0 2.31 1.82 -21.2% PASS
total/synth/SWAP8/s0 2.31 1.67 -27.9% PASS
total/synth/SWAP9/s0 2.32 1.83 -21.2% PASS
total/synth/XOR/b0 1.90 2.46 +29.7% PASS
total/synth/XOR/b1 2.12 2.23 +5.0% PASS
total/synth/loop_v1 6.96 7.42 +6.7% PASS
total/synth/loop_v2 6.92 7.41 +7.0% PASS

Summary: 194 benchmarks, 1 regressions


✅ Performance Check Passed (multipass)

Performance Benchmark Results (threshold: 20%)

Benchmark Baseline (us) Current (us) Change Status
total/main/blake2b_huff/8415nulls 1.71 2.10 +23.0% PASS
total/main/blake2b_huff/empty 0.13 0.13 +2.7% PASS
total/main/blake2b_shifts/8415nulls 6.54 6.54 -0.0% PASS
total/main/sha1_divs/5311 3.44 3.45 +0.4% PASS
total/main/sha1_divs/empty 0.05 0.05 +1.1% PASS
total/main/sha1_shifts/5311 3.76 3.73 -0.8% PASS
total/main/sha1_shifts/empty 0.06 0.06 -1.0% PASS
total/main/snailtracer/benchmark 63.73 69.67 +9.3% PASS
total/main/structarray_alloc/nfts_rank 0.31 0.32 +2.9% PASS
total/main/swap_math/insufficient_liquidity 0.03 0.03 -3.2% PASS
total/main/swap_math/received 0.03 0.03 -3.4% PASS
total/main/swap_math/spent 0.03 0.03 -3.5% PASS
total/main/weierstrudel/1 0.39 0.42 +5.6% PASS
total/main/weierstrudel/15 2.92 3.02 +3.6% PASS
total/micro/JUMPDEST_n0/empty 0.18 0.18 -2.6% PASS
total/micro/jump_around/empty 0.69 0.73 +5.4% PASS
total/micro/loop_with_many_jumpdests/empty 2.49 2.46 -1.1% PASS
total/micro/memory_grow_mload/by1 0.22 0.23 +2.9% PASS
total/micro/memory_grow_mload/by16 0.24 0.25 +2.2% PASS
total/micro/memory_grow_mload/by32 0.27 0.27 +1.4% PASS
total/micro/memory_grow_mload/nogrow 0.22 0.23 +3.2% PASS
total/micro/memory_grow_mstore/by1 0.25 0.27 +10.5% PASS
total/micro/memory_grow_mstore/by16 0.26 0.28 +6.4% PASS
total/micro/memory_grow_mstore/by32 0.27 0.29 +7.2% PASS
total/micro/memory_grow_mstore/nogrow 0.24 0.27 +10.1% PASS
total/micro/signextend/one 0.38 0.47 +23.6% PASS
total/micro/signextend/zero 0.38 0.46 +21.8% PASS
total/synth/ADD/b0 0.02 0.02 -3.5% PASS
total/synth/ADD/b1 0.02 0.02 -3.1% PASS
total/synth/ADDRESS/a0 0.99 1.14 +15.6% PASS
total/synth/ADDRESS/a1 1.02 1.17 +15.2% PASS
total/synth/AND/b0 0.02 0.02 -3.6% PASS
total/synth/AND/b1 0.02 0.02 -3.2% PASS
total/synth/BYTE/b0 1.97 2.00 +1.3% PASS
total/synth/BYTE/b1 2.32 2.32 +0.1% PASS
total/synth/CALLDATASIZE/a0 0.65 0.65 -0.1% PASS
total/synth/CALLDATASIZE/a1 0.68 0.68 -0.1% PASS
total/synth/CALLER/a0 1.15 1.14 -0.5% PASS
total/synth/CALLER/a1 1.17 1.17 -0.1% PASS
total/synth/CALLVALUE/a0 0.65 0.65 -0.1% PASS
total/synth/CALLVALUE/a1 0.68 0.68 -0.1% PASS
total/synth/CODESIZE/a0 0.65 0.65 +0.1% PASS
total/synth/CODESIZE/a1 0.68 0.68 +0.0% PASS
total/synth/DUP1/d0 0.02 0.02 -2.8% PASS
total/synth/DUP1/d1 0.02 0.02 -3.0% PASS
total/synth/DUP10/d0 0.02 0.02 -2.5% PASS
total/synth/DUP10/d1 0.02 0.02 -3.0% PASS
total/synth/DUP11/d0 0.02 0.02 -2.6% PASS
total/synth/DUP11/d1 0.02 0.02 -3.0% PASS
total/synth/DUP12/d0 0.02 0.02 -2.6% PASS
total/synth/DUP12/d1 0.02 0.02 -2.5% PASS
total/synth/DUP13/d0 0.02 0.02 -2.6% PASS
total/synth/DUP13/d1 0.02 0.02 -3.1% PASS
total/synth/DUP14/d0 0.02 0.02 -2.6% PASS
total/synth/DUP14/d1 0.02 0.02 -2.8% PASS
total/synth/DUP15/d0 0.02 0.02 -2.4% PASS
total/synth/DUP15/d1 0.02 0.02 -3.0% PASS
total/synth/DUP16/d0 0.02 0.02 -2.5% PASS
total/synth/DUP16/d1 0.02 0.02 -3.0% PASS
total/synth/DUP2/d0 0.02 0.02 -2.7% PASS
total/synth/DUP2/d1 0.02 0.02 -2.9% PASS
total/synth/DUP3/d0 0.02 0.02 -2.8% PASS
total/synth/DUP3/d1 0.02 0.02 -3.1% PASS
total/synth/DUP4/d0 0.02 0.02 -2.7% PASS
total/synth/DUP4/d1 0.02 0.02 -3.0% PASS
total/synth/DUP5/d0 0.02 0.02 -2.8% PASS
total/synth/DUP5/d1 0.02 0.02 -3.0% PASS
total/synth/DUP6/d0 0.02 0.02 -2.9% PASS
total/synth/DUP6/d1 0.02 0.02 -3.1% PASS
total/synth/DUP7/d0 0.02 0.02 -2.7% PASS
total/synth/DUP7/d1 0.02 0.02 -3.3% PASS
total/synth/DUP8/d0 0.02 0.02 -2.5% PASS
total/synth/DUP8/d1 0.02 0.02 -3.2% PASS
total/synth/DUP9/d0 0.02 0.02 -2.6% PASS
total/synth/DUP9/d1 0.02 0.02 -3.3% PASS
total/synth/EQ/b0 0.02 0.02 -3.3% PASS
total/synth/EQ/b1 0.02 0.02 -3.2% PASS
total/synth/GAS/a0 1.01 1.01 +0.1% PASS
total/synth/GAS/a1 1.05 1.05 +0.5% PASS
total/synth/GT/b0 0.02 0.02 -3.0% PASS
total/synth/GT/b1 0.02 0.02 -2.7% PASS
total/synth/ISZERO/u0 0.02 0.02 -3.9% PASS
total/synth/JUMPDEST/n0 0.18 0.18 +0.4% PASS
total/synth/LT/b0 0.02 0.02 -2.9% PASS
total/synth/LT/b1 0.02 0.02 -2.9% PASS
total/synth/MSIZE/a0 0.02 0.02 -4.0% PASS
total/synth/MSIZE/a1 0.02 0.02 -3.2% PASS
total/synth/MUL/b0 4.07 4.24 +4.4% PASS
total/synth/MUL/b1 4.14 4.29 +3.7% PASS
total/synth/NOT/u0 0.02 0.02 -3.9% PASS
total/synth/OR/b0 0.02 0.02 -3.3% PASS
total/synth/OR/b1 0.02 0.02 -3.1% PASS
total/synth/PC/a0 0.02 0.02 -4.0% PASS
total/synth/PC/a1 0.02 0.02 -3.8% PASS
total/synth/PUSH1/p0 0.02 0.02 -2.7% PASS
total/synth/PUSH1/p1 0.02 0.02 -3.4% PASS
total/synth/PUSH10/p0 0.04 0.04 +1.1% PASS
total/synth/PUSH10/p1 0.04 0.04 -1.1% PASS
total/synth/PUSH11/p0 0.04 0.05 +0.6% PASS
total/synth/PUSH11/p1 0.04 0.05 +0.5% PASS
total/synth/PUSH12/p0 0.05 0.05 +0.1% PASS
total/synth/PUSH12/p1 0.05 0.05 +0.1% PASS
total/synth/PUSH13/p0 0.05 0.05 +2.2% PASS
total/synth/PUSH13/p1 0.05 0.05 +1.2% PASS
total/synth/PUSH14/p0 0.05 0.05 +1.5% PASS
total/synth/PUSH14/p1 0.05 0.05 +0.9% PASS
total/synth/PUSH15/p0 0.06 0.06 +1.3% PASS
total/synth/PUSH15/p1 0.05 0.06 +0.4% PASS
total/synth/PUSH16/p0 0.06 0.06 +0.2% PASS
total/synth/PUSH16/p1 0.06 0.06 -0.0% PASS
total/synth/PUSH17/p0 0.06 0.06 +1.1% PASS
total/synth/PUSH17/p1 0.06 0.06 +0.6% PASS
total/synth/PUSH18/p0 0.06 0.06 +0.4% PASS
total/synth/PUSH18/p1 0.06 0.06 +0.4% PASS
total/synth/PUSH19/p0 0.07 0.07 +0.2% PASS
total/synth/PUSH19/p1 0.07 0.07 -0.1% PASS
total/synth/PUSH2/p0 0.02 0.02 -2.9% PASS
total/synth/PUSH2/p1 0.02 0.02 -3.0% PASS
total/synth/PUSH20/p0 0.07 0.07 -0.1% PASS
total/synth/PUSH20/p1 0.07 0.07 +0.0% PASS
total/synth/PUSH21/p0 0.07 0.07 +0.2% PASS
total/synth/PUSH21/p1 0.07 0.07 +0.9% PASS
total/synth/PUSH22/p0 1.80 1.32 -27.0% PASS
total/synth/PUSH22/p1 1.56 1.37 -12.3% PASS
total/synth/PUSH23/p0 1.81 1.32 -26.8% PASS
total/synth/PUSH23/p1 1.58 1.32 -16.3% PASS
total/synth/PUSH24/p0 1.81 1.32 -26.9% PASS
total/synth/PUSH24/p1 1.61 1.38 -14.2% PASS
total/synth/PUSH25/p0 1.81 1.33 -26.8% PASS
total/synth/PUSH25/p1 1.56 1.35 -13.6% PASS
total/synth/PUSH26/p0 1.81 1.33 -26.6% PASS
total/synth/PUSH26/p1 1.57 1.36 -13.0% PASS
total/synth/PUSH27/p0 1.82 1.33 -26.6% PASS
total/synth/PUSH27/p1 1.57 1.33 -15.7% PASS
total/synth/PUSH28/p0 1.82 1.37 -24.9% PASS
total/synth/PUSH28/p1 1.57 1.62 +3.0% PASS
total/synth/PUSH29/p0 1.82 1.34 -26.6% PASS
total/synth/PUSH29/p1 1.58 1.33 -15.5% PASS
total/synth/PUSH3/p0 0.02 0.02 -2.5% PASS
total/synth/PUSH3/p1 0.02 0.02 -2.7% PASS
total/synth/PUSH30/p0 1.83 1.35 -26.2% PASS
total/synth/PUSH30/p1 1.58 1.39 -12.1% PASS
total/synth/PUSH31/p0 1.86 1.34 -27.7% PASS
total/synth/PUSH31/p1 1.73 1.45 -16.2% PASS
total/synth/PUSH32/p0 1.83 1.34 -26.8% PASS
total/synth/PUSH32/p1 1.59 1.34 -15.5% PASS
total/synth/PUSH4/p0 0.03 0.03 -2.5% PASS
total/synth/PUSH4/p1 0.03 0.03 -2.3% PASS
total/synth/PUSH5/p0 0.03 0.03 -1.8% PASS
total/synth/PUSH5/p1 0.03 0.03 -2.1% PASS
total/synth/PUSH6/p0 0.03 0.03 -1.6% PASS
total/synth/PUSH6/p1 0.03 0.03 -1.3% PASS
total/synth/PUSH7/p0 0.03 0.03 -0.4% PASS
total/synth/PUSH7/p1 0.03 0.03 -1.5% PASS
total/synth/PUSH8/p0 0.04 0.04 -1.0% PASS
total/synth/PUSH8/p1 0.04 0.04 -1.3% PASS
total/synth/PUSH9/p0 0.04 0.04 +0.5% PASS
total/synth/PUSH9/p1 0.04 0.04 -0.3% PASS
total/synth/RETURNDATASIZE/a0 0.65 0.65 +0.0% PASS
total/synth/RETURNDATASIZE/a1 0.68 0.68 +0.1% PASS
total/synth/SAR/b0 3.44 3.56 +3.4% PASS
total/synth/SAR/b1 4.03 3.97 -1.5% PASS
total/synth/SGT/b0 0.02 0.02 -3.4% PASS
total/synth/SGT/b1 0.02 0.02 -3.1% PASS
total/synth/SHL/b0 3.67 3.96 +8.0% PASS
total/synth/SHL/b1 2.17 2.68 +23.5% PASS
total/synth/SHR/b0 2.98 3.11 +4.2% PASS
total/synth/SHR/b1 2.33 2.67 +14.6% PASS
total/synth/SIGNEXTEND/b0 2.06 2.45 +18.6% PASS
total/synth/SIGNEXTEND/b1 2.42 2.68 +10.9% PASS
total/synth/SLT/b0 0.02 0.02 -3.3% PASS
total/synth/SLT/b1 0.02 0.02 -3.5% PASS
total/synth/SUB/b0 0.02 0.02 -3.4% PASS
total/synth/SUB/b1 0.02 0.02 -3.3% PASS
total/synth/SWAP1/s0 0.02 0.01 -2.4% PASS
total/synth/SWAP10/s0 0.02 0.01 -2.0% PASS
total/synth/SWAP11/s0 0.02 0.01 -2.1% PASS
total/synth/SWAP12/s0 0.02 0.01 -2.3% PASS
total/synth/SWAP13/s0 0.02 0.01 -2.4% PASS
total/synth/SWAP14/s0 0.02 0.01 -2.2% PASS
total/synth/SWAP15/s0 0.02 0.01 -2.4% PASS
total/synth/SWAP16/s0 0.02 0.01 -2.6% PASS
total/synth/SWAP2/s0 0.02 0.01 -2.7% PASS
total/synth/SWAP3/s0 0.02 0.01 -2.7% PASS
total/synth/SWAP4/s0 0.02 0.01 -2.6% PASS
total/synth/SWAP5/s0 0.02 0.01 -2.8% PASS
total/synth/SWAP6/s0 0.02 0.01 -2.9% PASS
total/synth/SWAP7/s0 0.02 0.01 -4.8% PASS
total/synth/SWAP8/s0 0.02 0.01 -2.0% PASS
total/synth/SWAP9/s0 0.02 0.02 -1.3% PASS
total/synth/XOR/b0 0.02 0.02 -3.3% PASS
total/synth/XOR/b1 0.02 0.02 -3.2% PASS
total/synth/loop_v1 1.90 1.90 -0.2% PASS
total/synth/loop_v2 1.82 1.78 -2.3% PASS

Summary: 194 benchmarks, 0 regressions


…k test

Two fixes:

1. CREATE return data: On successful CREATE, the host returns the deployed
   contract code as output_data. The previous unconditional setReturnData
   from the result incorrectly exposed the contract code via RETURNDATASIZE,
   breaking 12 create_returndata state tests. Per the EVM spec (and geth),
   return data should be empty after successful CREATE. On REVERT or other
   failures, return data is correctly set from the sub-call output, matching
   evmone behavior for non-REVERT CREATE failure fuzz cases.

2. Fallback test: Update MultipleFallbackTriggers to expect
   EVMC_UNDEFINED_INSTRUCTION for opcode 0xEE, matching the opcode validity
   check added in the predecessor commit.

Made-with: Cursor
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes multiple interpreter-mode EVM correctness issues uncovered by differential fuzzing (gas accounting, opcode validity, call/create semantics, and return-data handling), aligning behavior more closely with EVMC/evmone expectations across EVM revisions and fallback execution paths.

Changes:

  • Remove stale static caching of EVMC instruction metrics in opcode gas calculation.
  • Fix CALL/CREATE/SELFDESTRUCT behavioral mismatches (CALL gas truncation, CREATE balance access warming, CREATE/SELFDESTRUCT return-data handling).
  • Adjust interpreter fallback opcode validity checks and gas-chunk boundaries (terminate chunks at SSTORE; use non-SPP block costs).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/tests/evm_fallback_execution_tests.cpp Updates expectation for invalid opcode handling to EVMC_UNDEFINED_INSTRUCTION.
src/evm/opcode_handlers.cpp Fixes gas metric table staleness and several CALL/CREATE/SELFDESTRUCT semantics.
src/evm/interpreter.cpp Adds opcode validity guard in the per-instruction fallback dispatch path.
src/evm/evm_cache.cpp Terminates gas chunks at SSTORE and uses correct per-block gas costs for SPP chunks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1202 to +1203
Context->setReturnData(std::vector<uint8_t>(
Result.output_data, Result.output_data + Result.output_size));
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result.output_data can be null when Result.output_size == 0 (several other call/create handling sites in the repo explicitly guard for this). Constructing a std::vector from a null pointer range is undefined behavior even if the range is empty. Please guard on Result.output_data != nullptr && Result.output_size > 0 (otherwise clear return data) before calling setReturnData() here.

Suggested change
Context->setReturnData(std::vector<uint8_t>(
Result.output_data, Result.output_data + Result.output_size));
if (Result.output_data != nullptr && Result.output_size > 0) {
Context->setReturnData(std::vector<uint8_t>(
Result.output_data, Result.output_data + Result.output_size));
} else {
Context->setReturnData(std::vector<uint8_t>());
}

Copilot uses AI. Check for mistakes.
@zoowii
Copy link
Contributor

zoowii commented Mar 3, 2026

conflict

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants