fix: [PM-19967] Bound GRANDPA and BEEFY finality subscription fan-out#1075
Open
fix: [PM-19967] Bound GRANDPA and BEEFY finality subscription fan-out#1075
Conversation
Introduces SubscriptionTracker with RAII-based SubscriptionGuard for enforcing configurable global limits on concurrent GRANDPA/BEEFY subscriptions. Includes SubscriptionMetrics for Prometheus monitoring (active gauge, rejected counter). Unit tests cover limit enforcement, guard lifecycle, and concurrent access. JIRA: https://shielded.atlassian.net/browse/PM-19967
Replace upstream GRANDPA and BEEFY subscription handlers with bounded versions that check a shared SubscriptionTracker before accepting new subscriptions. Non-subscription methods (roundState, proveFinality, getFinalizedHead) remain delegated to the upstream handlers. Each subscription acquires a SubscriptionGuard (RAII) that decrements the active count when the subscriber disconnects. The limit is configurable via --rpc-max-finality-subscriptions (default: 512). Includes Prometheus metrics registration for active and rejected subscription counts. JIRA: https://shielded.atlassian.net/browse/PM-19967
Use a barrier to synchronize thread acquisition so guards remain held when asserting active_count, preventing premature guard drops.
…into fix/PM-19967-unbounded-finality-subscription-fanout
- Remove unused SubscriptionBoundsConfig struct (dead code) - Extract duplicated GRANDPA/BEEFY subscription registration into generic register_bounded_finality_subscription helper - Add warn! log on subscription rejection for operator visibility - Await pending.reject() future to resolve unused-must-use warning
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.
Summary
Enforce a global limit on concurrent GRANDPA and BEEFY finality RPC subscriptions to prevent resource exhaustion from unbounded fan-out of consensus notifications. Addresses Medium-severity audit finding Issue W from the Least Authority security audit.
🎫 Ticket 📐 Engineering 🧪 Test Plan
Motivation
The node's GRANDPA and BEEFY RPC subscription handlers fan out consensus finality notifications via unbounded channels (
tracing_unbounded). An attacker can open many WebSocket connections, subscribe to finality streams, and let buffers grow without limit — degrading node responsiveness or triggering a crash. This was identified as Issue W (Medium severity) in the Least Authority initial audit report (October 2025), affecting the node at git revisiond204679f.While Substrate's jsonrpsee server exposes
--rpc-max-connectionsand--rpc-max-subscriptions-per-connectionflags, these are transport-level limits that do not bound the underlying notification channels from the consensus layer.Changes
subscription_boundsmodule (new) —SubscriptionTrackerusing atomic CAS for concurrent slot management,SubscriptionGuard(RAII) for automatic cleanup on client disconnect,SubscriptionMetricsfor Prometheus monitoring (midnight_rpc_finality_subscriptions_activegauge,midnight_rpc_finality_subscriptions_rejected_totalcounter)rpc.rs— Replace upstream GRANDPA and BEEFY subscription handlers with bounded variants using a merge-remove-replace pattern; genericregister_bounded_finality_subscription<T, TK>helper eliminates duplication between the two protocolscli.rs— New--rpc-max-finality-subscriptionsflag (default 512) for operator configurationservice.rs— Create tracker with optional Prometheus metrics registration, pass through to RPC builder📌 Submission Checklist
🔱 Fork Strategy
🗹 TODO before merging