Skip to content

Core EIP-7702 delegation parser accepts non-23-byte designators #1494

@chfast

Description

@chfast

Title

Malformed EIP-7702 designator length is treated as delegated code in core logic

Summary

evmone core delegation detection currently matches only the 3-byte prefix 0xef0100, without requiring the full 23-byte designator length (0xef0100 || address).

As a result, code like 0xef0100 (3 bytes) or 0xef0100 || address || extra (24+ bytes) is treated as delegated by evmone, while geth/revm use strict 23-byte parsing for delegation indicators.

Why this matters

EIP-7702 defines a delegation indicator as 0xef0100 || address (exactly 23 bytes). Prefix-only matching can alter call routing and transaction validity checks for accounts whose code merely starts with that prefix.

This is likely not exploitable on live networks through normal transaction flow (malformed indicators are not valid EIP-7702 designators, and evmone t8n/statetest prestate validation rejects invalid lengths), but it is still an execution-consistency bug in core/runtime logic that can affect custom genesis/state import/blockchain-test contexts.

Affected runtime paths:

  • lib/evmone/instructions_calls.cpp calls get_delegate_address() during CALL-family execution.
  • test/state/state.cpp uses is_code_delegated() for EIP-3607 sender checks and authorization processing.

Root cause

evmone

  • lib/evmone/delegation.hpp
    • is_code_delegated(bytes_view code) returns code.starts_with(DELEGATION_MAGIC).
  • lib/evmone/delegation.cpp
    • get_delegate_address() accepts any prefix match and copies whatever bytes follow, assuming full size.
    • In release builds, short/long designators are not rejected by this function.

geth

  • core/types/tx_setcode.go
    • ParseDelegation(b []byte) requires len(b) == 23 and prefix match.

revm

  • crates/bytecode/src/bytecode/mod.rs
    • new_eip7702_raw(bytes) rejects bytes.len() != 23 with InvalidLength.
    • eip7702_address() is only available for BytecodeKind::Eip7702.

Local reproductions

1) Direct core check in evmone

Build/run (from this session):

g++ -std=c++20 -I/home/chfast/proj/evmone -I/home/chfast/proj/evmone/evmc/include /tmp/check_delegate.cpp \
  -L/home/chfast/proj/evmone/build/debug/lib -levmone \
  -Wl,-rpath,/home/chfast/proj/evmone/build/debug/lib -o /tmp/check_delegate && /tmp/check_delegate

Observed:

short: delegated 0000000000000000000000000000000000000000
valid23: delegated 0000000000000000000000000000000000000011
long24: delegated 0000000000000000000000000000000000000011

Expected strict behavior: only valid23 should be delegated.

2) geth reference behavior

cd /tmp/checkgeth && go run .

Observed:

short: ok=false addr=0000000000000000000000000000000000000000
valid23: ok=true addr=0000000000000000000000000000000000000011
long24: ok=false addr=0000000000000000000000000000000000000000

3) t8n observable divergence (input handling)

Fixture in:

files/case22-prague-delegation-shortcode/

Running Prague t8n on that fixture:

  • geth accepts and executes
  • evmone-t8n rejects with:
    EIP-7702 delegation designator at ... has invalid size

This confirms strict-length expectation exists in evmone test input validation, but core delegation helper itself still performs prefix-only matching.

4) Blockchain-test style semantic repro with malformed long designator

Fixture directory:

files/case23-prague-delegation-longcode/

  • Delegated account code is malformed long form:
    0xef0100 || 20-byte-target || 0x00 (24 bytes total).
  • Target code does SSTORE(0,1).

Reference transition (geth):

cd files/case23-prague-delegation-longcode
/home/chfast/go/bin/evm t8n \
  --state.fork Prague --state.chainid 1 \
  --input.alloc alloc.json --input.env env.json --input.txs txs.json \
  --output.basedir out-geth --output.result result.json --output.alloc alloc.json

Observed in out-geth/result.json:

  • gasUsed = 0x186a0
  • stateRoot = 0x463f7388...45e2435
  • receipt status 0x0 (invalid opcode semantics on 0xef... code)

evmone execution on equivalent block fixture:

/home/chfast/proj/evmone/build/debug/bin/evmone-blockchaintest \
  files/case23-prague-delegation-longcode/blocktest_longcode.json

Observed:

  • different stateRoot = 0x7451ee8f...301421dd
  • different gas_used = 43106
  • resulting state shows 0x095e...d87 storage slot 0x00 -> 0x01

This demonstrates runtime call-routing divergence from malformed long designators (not just parser-level rejection).

Suggested fix

Require exact length in core delegation detection:

  • either change is_code_delegated() to enforce size == 23 and prefix
  • or add a separate strict helper for places that require a valid designator (CALL routing, sender validation, authorization checks)

and keep behavior consistent across:

  • lib/evmone/delegation.*
  • test/state/state.cpp
  • test/utils/statetest_loader.cpp

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions