Skip to content

fix: bound remote pubsub subscriptions to prevent memory exhaustion - #1350

Open
acul71 wants to merge 5 commits into
mainfrom
fix/gossipsub-subscription-limits
Open

fix: bound remote pubsub subscriptions to prevent memory exhaustion#1350
acul71 wants to merge 5 commits into
mainfrom
fix/gossipsub-subscription-limits

Conversation

@acul71

@acul71 acul71 commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fix memory exhaustion / DoS via unbounded remote topic subscriptions (GHSA-4f8r-922h-2vgv).
  • Delete empty peer_topics entries on unsubscribe and peer removal (disconnect / blacklist).
  • Enforce inbound RPC size (1 MiB) and per-RPC / per-peer subscription limits (5000 / 100 by default).

Fixes #1349

Related

Test plan

  • pytest tests/core/pubsub/test_pubsub_subscription_limits.py -v
  • pytest tests/core/pubsub/test_pubsub.py::test_handle_subscription -v
  • pytest tests/core/pubsub/test_pubsub.py::test_peers_subscribe -v
  • make lint && make typecheck && make test && make linux-docs

Made with Cursor

@acul71

acul71 commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

Status update — GHSA-4f8r-922h-2vgv / #1349

cc @seetadev @sumanjeet0012 for review — this addresses the GossipSub subscription-flood memory exhaustion pattern reported across the libp2p ecosystem (js GHSA-4f8r-922h-2vgv, go go-libp2p-pubsub#710, cpp cpp-libp2p#349).

What’s been accomplished

Core mitigations (shared Pubsub layer — applies to GossipSub and FloodSub):

  1. Empty topic map cleanuppeer_topics entries are removed when the last peer unsubscribes or disconnects (_remove_peer_from_topic, _clear_peer_from_all_topics), matching go-libp2p-pubsub Implement validation throttler for message validation in Pubsub #710.
  2. Inbound RPC size limit — 1 MiB default on pubsub read (read_varint_prefixed_bytes + max_inbound_rpc_size), aligned with outbound DefaultMaxMessageSize.
  3. Per-RPC subscription cap — default 5000 entries/RPC (js-libp2p post-15.0.23 default); over-limit RPCs are rejected.
  4. Per-peer subscription cap — default 100 distinct topics/peer (go downstream / Filecoin-style limit); excess subscribes are ignored with a warning.
  5. Shutdown cleanuppeer_topics and per-peer subscription counts cleared in Pubsub.run() finally (js topics.clear() parity).
  6. Optional allowed_topics allowlist — remote subscribes outside the set are ignored (js allowedTopics / go allowlist filter parity).

Teststests/core/pubsub/test_pubsub_subscription_limits.py (12 cases), plus updates to test_handle_subscription and test_peers_subscribe. Coverage includes:

  • Unsubscribe / disconnect / _handle_dead_peer / blacklist teardown cleanup
  • Per-RPC and per-peer limits (reject + at-limit accept)
  • Oversized inbound frames
  • Idempotent resubscribe counting
  • allowed_topics accept/reject/unsubscribe

CImake lint, make typecheck, make test (2893 passed), make linux-docs all green locally.

What’s still missing / out of scope for this PR

Gap Notes
PoC-scale regression test No js-style flood test (~349k unique topics / heap measurement). Current tests use small N; sufficient for CI regression, not a full advisory reproduction.
Full resource manager #572 remains open; this PR does not implement rcmgr-style global limits.
Score / blacklist on violation Subscription floods are logged and dropped; no GossipSub scorer penalty or automatic blacklist (deferred for interoperability).
Inbound frame size vs js py-libp2p caps pubsub RPC at 1 MiB; js advisory references 4 MiB LP frames — still blocks single-frame mega-RPC, but not identical.
Rich subscription filter API allowed_topics covers the allowlist case; no full go-style SubscriptionFilter plugin interface.

Ecosystem parity (rough)

Happy to adjust default limits, add scorer penalties, or scope a follow-up PoC test if maintainers prefer. Thanks for any guidance.

acul71 and others added 5 commits August 10, 2026 23:47
Fix GHSA-4f8r-922h-2vgv by enforcing inbound RPC size and subscription
limits, tracking per-peer subscription counts, and deleting empty topic
entries from peer_topics on unsubscribe and disconnect.

Fixes #1349

Co-authored-by: Cursor <cursoragent@cursor.com>
Clear peer_topics on Pubsub shutdown, add optional allowed_topics
filter, and expand regression tests for dead-peer teardown, blacklist
cleanup, RPC boundary cases, and idempotent resubscribe.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use main's forget helpers, assert oversize peer teardown, and replace
fixed sleeps with condition waits for more reliable CI.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Empty topic map entries are now deleted by _forget_*; update assertions
to use .get() / key absence instead of indexing removed topics.

Co-authored-by: Cursor <cursoragent@cursor.com>
@acul71
acul71 force-pushed the fix/gossipsub-subscription-limits branch from 2034748 to 957fa6a Compare August 10, 2026 21:57
@acul71

acul71 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

Status update — rebased onto main (blockers resolved)

cc @seetadev @sumanjeet0012 — branch has been rebased onto current main and force-pushed (--force-with-lease). Sync is now 0 behind / 5 ahead, with no merge conflicts.

What changed vs prior tip

  1. Rebased onto main; folded limits/cleanup into main’s _forget_subscription / _forget_all_subscriptions / _replay_recent_messages (kept metrics + mcache replay).
  2. Oversize inbound RPC now logs, resets the stream, and calls _handle_dead_peer (no auto-blacklist).
  3. Empty topic keys deleted on forget; subscription counts popped at zero.
  4. Tests retargeted to _forget_*, assert peer teardown on oversize, condition waits instead of fixed sleeps; updated replay-gate assertions for empty-key deletion.

Validation (local)

Ready for another look when convenient. Thanks!

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.

Security: GossipSub memory exhaustion via unbounded remote topic subscriptions

1 participant