Summary
A potential memory exhaustion vulnerability has been identified in cpp-libp2p's GossipSub implementation. A remote peer may be able to cause unbounded growth of gossip routing state by sending a large number of unique topic subscription entries.
The issue arises because incoming subscription lists are processed without truncation and there is currently no limit on the number of distinct topics a single peer may subscribe to. As a result, attacker-controlled topic identifiers can accumulate indefinitely in per-peer and global subscription tracking structures.
This issue was responsibly reported by @tonghuaroot and verified by @seetadev through private disclosure. We thank them for their careful analysis, responsible reporting process, and ecosystem-wide review of related GossipSub implementations.
Affected Component
- GossipSub
- Remote subscription tracking
- Message subscription processing
Affected areas include:
- src/protocol/gossip/impl/message_parser.cpp
- src/protocol/gossip/impl/gossip_core.cpp
- src/protocol/gossip/impl/remote_subscriptions.cpp
- src/protocol/gossip/impl/peer_context.hpp
Impact
A connected peer can continuously submit SUBSCRIBE messages containing previously unseen topic identifiers.
Because these topic identifiers are stored without an effective upper bound:
- Per-peer subscription state grows indefinitely.
- Global topic subscription structures grow indefinitely.
- Memory consumption increases linearly with attacker input.
- Nodes may eventually experience memory pressure, degraded performance, or process termination.
This issue affects availability and may enable a denial-of-service attack against exposed GossipSub nodes.
Attack Requirements
Unbounded Subscription Processing
Incoming protobuf subscription entries are forwarded directly to subscription handling logic without limiting the number of entries processed from a single RPC message.
Current logic iterates over every subscription entry and forwards it to the receiver:
for (const auto &s : pb_msg_->subscriptions()) {
receiver.onSubscription(from, s.subscribe(), s.topicid());
}
Unbounded Per-Peer Topic Tracking
Subscription handling inserts topics into:
peer->subscribed_to
which is implemented as:
std::set
with no maximum size.
When a previously unseen topic is received:
auto res = getItem(topic, true);
creates a corresponding TopicSubscriptions entry.
As a result, a malicious peer can continuously introduce new topic identifiers, causing both:
- Growth of the peer's subscription set.
- Growth of the global topic subscription map.
Reproduction Concept
A peer repeatedly sends GossipSub SUBSCRIBE messages containing unique topic identifiers:
topic-1
topic-2
topic-3
topic-N
Each new topic causes additional memory allocation and state retention.
The growth is proportional to the number of unique topics accepted by the victim node.
Related Ecosystem Findings
This issue is closely related to previously identified GossipSub subscription-flooding vulnerabilities in other libp2p implementations.
In particular, js-libp2p introduced protections including:
- Per-RPC subscription limits.
- Per-peer topic subscription limits.
The same defense-in-depth approach is applicable here.
Recommended Remediation
- Add Per-RPC Subscription Limits
Introduce a maximum number of subscription entries processed from a single incoming RPC message.
Example:
static constexpr size_t kMaxSubscriptionsPerRpc = ...;
Additional entries should be ignored or rejected.
2. Add Per-Peer Topic Limits
Introduce a maximum number of accumulated topic subscriptions per peer.
Example:
static constexpr size_t kMaxSubscribedTopicsPerPeer = 16384;
Before inserting a new topic:
if (peer->subscribed_to.size() >= kMaxSubscribedTopicsPerPeer &&
peer->subscribed_to.count(topic) == 0) {
return;
}
This prevents memory growth across multiple RPC messages.
3. Add Regression Tests
Tests should verify:
- Large subscription floods are truncated.
- Per-peer topic limits are enforced.
- Memory growth remains bounded.
- Existing unsubscribe behavior continues to function correctly.
Severity Assessment
Availability impact is potentially high because attacker-controlled input can trigger unbounded memory growth.
Provisional severity assessment:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CVSS Score: 7.5 (High)
Acknowledgements
Special thanks to @tonghuaroot for responsibly reporting this issue, providing detailed source-level analysis, identifying parallels across multiple libp2p implementations, and helping improve the resilience of the broader libp2p ecosystem. The issue was verified by @seetadev.
Summary
A potential memory exhaustion vulnerability has been identified in cpp-libp2p's GossipSub implementation. A remote peer may be able to cause unbounded growth of gossip routing state by sending a large number of unique topic subscription entries.
The issue arises because incoming subscription lists are processed without truncation and there is currently no limit on the number of distinct topics a single peer may subscribe to. As a result, attacker-controlled topic identifiers can accumulate indefinitely in per-peer and global subscription tracking structures.
This issue was responsibly reported by @tonghuaroot and verified by @seetadev through private disclosure. We thank them for their careful analysis, responsible reporting process, and ecosystem-wide review of related GossipSub implementations.
Affected Component
Affected areas include:
Impact
A connected peer can continuously submit SUBSCRIBE messages containing previously unseen topic identifiers.
Because these topic identifiers are stored without an effective upper bound:
This issue affects availability and may enable a denial-of-service attack against exposed GossipSub nodes.
Attack Requirements
Unbounded Subscription Processing
Incoming protobuf subscription entries are forwarded directly to subscription handling logic without limiting the number of entries processed from a single RPC message.
Current logic iterates over every subscription entry and forwards it to the receiver:
for (const auto &s : pb_msg_->subscriptions()) {
receiver.onSubscription(from, s.subscribe(), s.topicid());
}
Unbounded Per-Peer Topic Tracking
Subscription handling inserts topics into:
peer->subscribed_to
which is implemented as:
std::set
with no maximum size.
When a previously unseen topic is received:
auto res = getItem(topic, true);
creates a corresponding TopicSubscriptions entry.
As a result, a malicious peer can continuously introduce new topic identifiers, causing both:
Reproduction Concept
A peer repeatedly sends GossipSub SUBSCRIBE messages containing unique topic identifiers:
topic-1
topic-2
topic-3
topic-N
Each new topic causes additional memory allocation and state retention.
The growth is proportional to the number of unique topics accepted by the victim node.
Related Ecosystem Findings
This issue is closely related to previously identified GossipSub subscription-flooding vulnerabilities in other libp2p implementations.
In particular, js-libp2p introduced protections including:
The same defense-in-depth approach is applicable here.
Recommended Remediation
Introduce a maximum number of subscription entries processed from a single incoming RPC message.
Example:
static constexpr size_t kMaxSubscriptionsPerRpc = ...;
Additional entries should be ignored or rejected.
2. Add Per-Peer Topic Limits
Introduce a maximum number of accumulated topic subscriptions per peer.
Example:
static constexpr size_t kMaxSubscribedTopicsPerPeer = 16384;
Before inserting a new topic:
if (peer->subscribed_to.size() >= kMaxSubscribedTopicsPerPeer &&
peer->subscribed_to.count(topic) == 0) {
return;
}
This prevents memory growth across multiple RPC messages.
3. Add Regression Tests
Tests should verify:
Severity Assessment
Availability impact is potentially high because attacker-controlled input can trigger unbounded memory growth.
Provisional severity assessment:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CVSS Score: 7.5 (High)
Acknowledgements
Special thanks to @tonghuaroot for responsibly reporting this issue, providing detailed source-level analysis, identifying parallels across multiple libp2p implementations, and helping improve the resilience of the broader libp2p ecosystem. The issue was verified by @seetadev.