security: fixes p2p memory DoS via crafted response messages (CVE-2026-26313)#35
Open
chris-mercer wants to merge 3 commits into
Open
security: fixes p2p memory DoS via crafted response messages (CVE-2026-26313)#35chris-mercer wants to merge 3 commits into
chris-mercer wants to merge 3 commits into
Conversation
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>
There was a problem hiding this comment.
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
ethandsnapresponse messages. - Wire validation into
ethandsnapmessage 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.
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
maxMessageSizebyte-size check passes, butmsg.Decodeallocates 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.Decoderuns, the raw message payload (already buffered in memory by the transport layer) is scanned usingrlp.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 likeTransactionsMsgandNewBlockHashesMsg, the outer list items are counted directly.Changes
eth/protocols/eth/msgvalidate.go: Pre-decode item count validation for eth protocol messageseth/protocols/eth/handler.go: Wire validation intohandleMessagebetween size check and handler dispatcheth/protocols/snap/msgvalidate.go: Pre-decode item count validation for snap protocol messageseth/protocols/snap/handler.go: Wire validation intoHandleMessagebetween size check and message switchValidated messages
eth protocol:
BlockHeadersMsg,BlockBodiesMsg,ReceiptsMsg,PooledTransactionsMsg— limit: 2048 (2x serve-sidemaxHeadersServe/maxBodiesServe)TransactionsMsg— limit: 32768 (matchesmaxKnownTxs)NewBlockHashesMsg— limit: 2048snap protocol:
AccountRangeMsg,StorageRangesMsg,ByteCodesMsg,TrieNodesMsg— limit: 2048 (2x serve-sidemaxCodeLookups/maxTrieNodeLookups)Vulnerability
Approach: Minimal port
The full upstream fix (go-ethereum PR #33835) is a 26-file, 1,100+ line refactor that restructures
BlockBodyfields torlp.RawList[T], introduces genericReceiptsPacket[L]types, refactorsp2p/trackerfrom global to per-peer, and changesDeliverBodiessignatures. Cherry-picking produces 13 merge conflicts including 2 modify/delete (core-geth deletedreceipt.gowhich 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.Splitandrlp.CountValueswhich exist on the current Go 1.21 codebase.Verification
References
Road to Olympia — Core-Geth Modernization March
Developed by White B0x Inc. for Ethereum Classic DAO LLC
🤖 Generated with Claude Code
Merge Order