Skip to content

fix: set finite gossipsub decode limits for control messages#9117

Open
lodekeeper wants to merge 3 commits intoChainSafe:unstablefrom
lodekeeper:fix/gossipsub-decode-limits
Open

fix: set finite gossipsub decode limits for control messages#9117
lodekeeper wants to merge 3 commits intoChainSafe:unstablefrom
lodekeeper:fix/gossipsub-decode-limits

Conversation

@lodekeeper
Copy link
Copy Markdown
Contributor

@lodekeeper lodekeeper commented Mar 26, 2026

Motivation

js-gossipsub defaults all protobuf decode limits (maxSubscriptions, maxMessages, maxIwantMessageIDs, etc.) to Infinity. This means a peer can craft gossipsub RPCs with arbitrarily large control message arrays that are fully decoded into memory before any application-level caps apply.

Description

Set finite decodeRpcLimits in the gossipsub configuration as defense-in-depth against resource exhaustion via crafted control messages.

Limits set

Limit Value What it actually bounds Rationale
maxSubscriptions 512 SubOpts[] entries per RPC Covers a peer sending its full current subscription set on stream attach with plenty of headroom for Ethereum topic growth
maxMessages 5000 Message[] entries per RPC Current js-libp2p handleIWant() can serialize many RPC.Messages into one response RPC, so this must remain high until send-side chunking / byte caps are added
maxIhaveMessageIDs 256 outer ControlIHave[] entries per RPC Upstream field name is misleading; this is not nested IHAVE messageIDs. Honest behavior is bounded by topic count, not thousands
maxIwantMessageIDs 16 outer ControlIWant[] entries per RPC Upstream field name is misleading; this is not nested IWANT messageIDs. js-libp2p normally emits a single outer IWANT object
maxIdontwantMessageIDs 16 nested messageIDs per ControlIDontWant Honest behavior is tiny (often 1); keeping this small avoids outer×inner decode explosions
maxControlMessages 256 outer GRAFT/PRUNE/IDONTWANT entries per RPC Enough room for multi-topic control bursts without leaving an excessively large decode surface
maxPeerInfos 32 PX peer infos per ControlPrune Close to GossipsubPrunePeers = 16 while leaving some interop slack

Context

Lighthouse v8.1.3 patched analogous unbounded IWANT/IDONTWANT vulnerabilities in rust-libp2p (CVEs pending disclosure). While js-gossipsub has some handler-level caps (IHAVE and IDONTWANT), the protobuf decode layer is the first line of defense and should have finite bounds.

Important caveat

maxIhaveMessageIDs / maxIwantMessageIDs are upstream API names, but in js-libp2p they currently bound the number of outer ControlIHave / ControlIWant entries, not nested message IDs. Nested IHAVE/IWANT messageIDs still need a follow-up upstream/local fix if we want complete protobuf-level coverage.

js-gossipsub defaults all protobuf decode limits (maxSubscriptions,
maxMessages, maxIwantMessageIDs, etc.) to Infinity. This means an
attacker can craft gossipsub RPCs with arbitrarily large control
message arrays that are fully decoded into memory before any
application-level caps apply.

Set finite decode limits as defense-in-depth against resource
exhaustion via crafted control messages. Values are generous enough
for normal Ethereum CL gossipsub operation while preventing abuse.

Context: Lighthouse v8.1.3 patched analogous unbounded IWANT/IDONTWANT
vulnerabilities in rust-libp2p (CVEs pending disclosure).
@lodekeeper lodekeeper requested a review from a team as a code owner March 26, 2026 21:27
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces decodeRpcLimits to the Gossipsub configuration as a defense-in-depth measure against resource exhaustion from untrusted RPC messages. Feedback was provided to extract the hardcoded limit values into named constants to improve maintainability and readability.

Comment on lines +186 to +194
decodeRpcLimits: {
maxSubscriptions: 512,
maxMessages: 256,
maxIhaveMessageIDs: 200,
maxIwantMessageIDs: 200,
maxIdontwantMessageIDs: 2000,
maxControlMessages: 500,
maxPeerInfos: 100,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For better maintainability and readability, it's good practice to extract these magic numbers into named constants. This makes it easier to understand their purpose and to modify them in the future.

You could define these constants at the top of the file, for example after MAX_OUTBOUND_BUFFER_SIZE, and include the excellent rationale from the PR description as comments for each constant.

Example:

/** Well above the ~140 CL topics (global + attestation subnets + sync subnets + blob/column topics) */
const DECODE_RPC_MAX_SUBSCRIPTIONS = 512;
/** Generous for burst messages (attestations, blob sidecars) */
const DECODE_RPC_MAX_MESSAGES = 256;
// ... and so on for the other limits
Suggested change
decodeRpcLimits: {
maxSubscriptions: 512,
maxMessages: 256,
maxIhaveMessageIDs: 200,
maxIwantMessageIDs: 200,
maxIdontwantMessageIDs: 2000,
maxControlMessages: 500,
maxPeerInfos: 100,
},
decodeRpcLimits: {
maxSubscriptions: DECODE_RPC_MAX_SUBSCRIPTIONS,
maxMessages: DECODE_RPC_MAX_MESSAGES,
maxIhaveMessageIDs: DECODE_RPC_MAX_IHAVE_MESSAGE_IDS,
maxIwantMessageIDs: DECODE_RPC_MAX_IWANT_MESSAGE_IDS,
maxIdontwantMessageIDs: DECODE_RPC_MAX_IDONTWANT_MESSAGE_IDS,
maxControlMessages: DECODE_RPC_MAX_CONTROL_MESSAGES,
maxPeerInfos: DECODE_RPC_MAX_PEER_INFOS,
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For a single 7-value object literal with a descriptive comment block and a rationale table in the PR body, I think the inline values are clear enough. Extracting to file-level constants would roughly double the line count without adding much readability — the values are only referenced in this one place. Keeping as-is.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39958d2e18

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +189 to +190
maxIhaveMessageIDs: 200,
maxIwantMessageIDs: 200,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Raise IHAVE/IWANT decode limits to protocol-compatible values

Capping maxIhaveMessageIDs and maxIwantMessageIDs at 200 is likely too low for valid gossipsub traffic under mainnet load, where honest peers can include far more message IDs in a single IHAVE/IWANT control message. Because this cap is enforced during protobuf decoding, oversized-but-valid control messages are rejected before normal gossip handling, which can drop propagation and create avoidable peer churn. Please set these decode limits to values aligned with the protocol/runtime control-message maxima rather than a low fixed value.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. GossipsubMaxIHaveLength = 5000 in js-gossipsub means honest peers can include up to 5000 message IDs in IHAVE/IWANT per heartbeat. A decode limit of 200 would silently truncate legitimate messages at the protobuf layer before the runtime caps apply. Raised both to 5000 in 7a02bc2 to align with the protocol constant.

maxIhaveMessageIDs and maxIwantMessageIDs at 200 would truncate honest
peers that send up to GossipsubMaxIHaveLength (5000) message IDs per
heartbeat. Raise both to 5000 to align decode-time bounds with the
runtime protocol constant.
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.

1 participant