Skip to content

security: fixes p2p memory DoS via crafted response messages (CVE-2026-26313)#35

Open
chris-mercer wants to merge 3 commits into
ethereumclassic:masterfrom
chris-mercer:security/cve-2026-26313
Open

security: fixes p2p memory DoS via crafted response messages (CVE-2026-26313)#35
chris-mercer wants to merge 3 commits into
ethereumclassic:masterfrom
chris-mercer:security/cve-2026-26313

Conversation

@chris-mercer
Copy link
Copy Markdown
Member

Security: Pre-decode item count validation for eth and snap protocols (CVE-2026-26313)

A malicious peer can send a p2p response (BlockHeaders, BlockBodies, Receipts, PooledTransactions, Transactions) with a valid RLP list header claiming millions of tiny items. The existing 10 MiB maxMessageSize byte-size check passes, but msg.Decode allocates pointer/struct objects proportional to item count via reflection before the response is validated — 10 MiB of 1-byte RLP items (~10M items) causes OOM.

How it works

Before msg.Decode runs, the raw message payload (already buffered in memory by the transport layer) is scanned using rlp.CountValues — a zero-allocation operation that reads only RLP tag bytes. If the item count exceeds the protocol limit, the message is rejected immediately with a disconnect.

For request-ID wrapped responses ([requestId, [items...]]), the validation skips the RequestId field and counts the inner list. For flat broadcasts like TransactionsMsg and NewBlockHashesMsg, the outer list items are counted directly.

Changes

  • eth/protocols/eth/msgvalidate.go: Pre-decode item count validation for eth protocol messages
  • eth/protocols/eth/handler.go: Wire validation into handleMessage between size check and handler dispatch
  • eth/protocols/snap/msgvalidate.go: Pre-decode item count validation for snap protocol messages
  • eth/protocols/snap/handler.go: Wire validation into HandleMessage between size check and message switch

Validated messages

eth protocol:

  • BlockHeadersMsg, BlockBodiesMsg, ReceiptsMsg, PooledTransactionsMsg — limit: 2048 (2x serve-side maxHeadersServe/maxBodiesServe)
  • TransactionsMsg — limit: 32768 (matches maxKnownTxs)
  • NewBlockHashesMsg — limit: 2048

snap protocol:

  • AccountRangeMsg, StorageRangesMsg, ByteCodesMsg, TrieNodesMsg — limit: 2048 (2x serve-side maxCodeLookups/maxTrieNodeLookups)

Vulnerability

ID Severity Description
CVE-2026-26313 High DoS via crafted p2p message — attacker causes OOM through response messages with millions of tiny items

Approach: Minimal port

The full upstream fix (go-ethereum PR #33835) is a 26-file, 1,100+ line refactor that restructures BlockBody fields to rlp.RawList[T], introduces generic ReceiptsPacket[L] types, refactors p2p/tracker from global to per-peer, and changes DeliverBodies signatures. Cherry-picking produces 13 merge conflicts including 2 modify/delete (core-geth deleted receipt.go which upstream modified), making a clean backport infeasible.

This minimal port addresses the core vulnerability — OOM from crafted responses — with ~120 lines of validation code and zero structural changes. It uses only rlp.Split and rlp.CountValues which exist on the current Go 1.21 codebase.

Verification

go build ./eth/protocols/eth/... ./eth/protocols/snap/...   #
go vet ./eth/protocols/eth/... ./eth/protocols/snap/...     #
go test ./eth/protocols/eth/... -run TestValidateMessage -v # ✅ 10 tests
go test ./eth/protocols/snap/... -run TestValidateSnap -v   # ✅ 9 tests

References


Road to Olympia — Core-Geth Modernization March

Developed by White B0x Inc. for Ethereum Classic DAO LLC

🤖 Generated with Claude Code

Merge Order

chris-mercer and others added 2 commits March 21, 2026 00:45
A malicious peer can send a p2p response with a valid RLP list header
claiming millions of tiny items. The 10 MiB maxMessageSize check passes,
but msg.Decode allocates pointer/struct objects proportional to item count
before the response is validated, causing OOM.

This adds pre-decode item count validation for both eth and snap protocols.
Before msg.Decode runs, the raw message payload (already buffered by the
transport layer) is scanned using rlp.CountValues — a zero-allocation
operation that reads only RLP tag bytes. If the item count exceeds the
protocol limit, the message is rejected immediately.

Validated eth messages: BlockHeaders, BlockBodies, Receipts,
PooledTransactions, Transactions, NewBlockHashes.
Validated snap messages: AccountRange, StorageRanges, ByteCodes, TrieNodes.

Limits: 2048 for responses (2x serve-side maxHeadersServe/maxBodiesServe),
32768 for transaction broadcasts (matches maxKnownTxs).

This is a minimal port of upstream go-ethereum PR #33835. The full upstream
refactor (delayed message decoding with rlp.RawList) cannot be cleanly
backported due to structural divergence (13 merge conflicts).

References:
- CVE-2026-26313: GHSA-689v-6xwf-5jf3
- Upstream fix: ethereum/go-ethereum#33835
- Issue etclabscore#692: etclabscore#692

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests for the CVE-2026-26313 mitigation covering:
- Unvalidated message types pass through (nil, nil)
- Request-ID wrapped messages below/at/above limits
- Flat broadcast messages (Transactions, NewBlockHashes)
- All validated message codes reject oversized payloads
- Malformed RLP passes through to normal decoder
- Empty response lists pass validation
- Snap responses with proof fields (AccountRange, StorageRanges)

19 tests total (10 eth + 9 snap).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

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

This PR mitigates CVE-2026-26313 by pre-validating RLP item counts for selected eth and snap protocol messages before msg.Decode runs, preventing reflection-driven allocation blowups from crafted “many tiny items” payloads.

Changes:

  • Add pre-decode RLP list item-count validators for eth and snap response messages.
  • Wire validation into eth and snap message handlers between size checks and decode/dispatch.
  • Add unit tests covering below/at/above-limit, malformed RLP pass-through, and message-type coverage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
eth/protocols/eth/msgvalidate.go Adds eth message item-count validation via rlp.Split + rlp.CountValues.
eth/protocols/eth/msgvalidate_test.go Tests eth validation behavior and limits.
eth/protocols/eth/handler.go Invokes eth validation prior to handler dispatch and rewinds payload.
eth/protocols/snap/msgvalidate.go Adds snap response item-count validation on the primary list after RequestId.
eth/protocols/snap/msgvalidate_test.go Tests snap validation behavior and limits.
eth/protocols/snap/handler.go Invokes snap validation prior to message switch and rewinds payload.

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

Comment thread eth/protocols/eth/msgvalidate.go
Comment thread eth/protocols/eth/msgvalidate.go Outdated
Comment thread eth/protocols/snap/msgvalidate.go
Comment thread eth/protocols/snap/msgvalidate.go Outdated
Replace rlp.CountValues with countValuesExceedsLimit that exits as
soon as the limit is exceeded, avoiding 10M iterations for attack
payloads with millions of tiny items.

Document that this fix validates only top-level item counts; inner
lists (e.g., transactions within a BlockBody) are bounded by the
10 MiB maxMessageSize. A future full backport of upstream's
rlp.RawList delayed decoding would address inner lists as well.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

2 participants