Skip to content

fix(publisher): cap concurrent playback connections to prevent resource-exhaustion DoS#18

Merged
ntt0601zcoder merged 1 commit into
mainfrom
fix/playback-conn-cap
Jun 13, 2026
Merged

fix(publisher): cap concurrent playback connections to prevent resource-exhaustion DoS#18
ntt0601zcoder merged 1 commit into
mainfrom
fix/playback-conn-cap

Conversation

@ntt0601zcoder

Copy link
Copy Markdown
Owner

Problem (audit A-1, HIGH)

The long-lived play/push protocols (RTSP, RTMP, SRT, HTTP-MPEGTS) each allocate heavyweight per-connection state — a demux pipeline, a multi-MB write/ts buffer, 2-3 goroutines, an FD — with no cap. None of the handlers authenticate (the token param is attribution-only), so an unauthenticated client that knows one active stream code can open many concurrent connections and exhaust goroutines / FDs / memory, OOM-killing the shared process and taking every stream on the host down.

Fix

New connLimiter (internal/publisher/conn_limiter.go) on the publisher Service caps concurrent playback connections per-stream and globally, checked before any per-connection state is allocated, released on close:

Handler Acquire → reject Release
HandleMPEGTS 503 defer
HandleRTMPPlay error defer
srtHandleSubscribe close + return defer (moved to the subscribe path so it's leak-proof)
RTSP OnPlay 503 OnSessionClose via the rtspSessions entry, with a repeated-PLAY guard (PAUSE→PLAY can't double-count) + nil-ps cap-only entries handled by the touch loop / pollBytes

Caps are configurable — PublisherConfig.MaxPlaybackConnPerStream / MaxPlaybackConnTotal: unset → protective default (256 / 4096), negative → unlimited. HLS/DASH viewers go over stateless HTTP and are not counted here.

The acquire/release balance across all four handlers was adversarially audited (including against gortsplib's serialized-per-session + guaranteed-OnSessionClose lifecycle for the non-defer RTSP path) — no slot-leak or double-release found, and release itself is underflow-safe.

Test

TestConnLimiter_* (per-stream cap, global cap, per-stream isolation, unlimited, spurious-release no-underflow, concurrent -race balance back to zero), TestResolvePlaybackCap, TestHandleMPEGTS_PlaybackCapRejectsAndReleases (2nd conn over cap → 503; slot frees after the first closes).

go test -race ./internal/publisher/ green, golangci-lint 0 issues, full go build ./... green.

Note: this is the resource-cap half of A-1; authentication (the other half) remains tracked as S-1.

…ce-exhaustion DoS

The long-lived play/push protocols (RTSP, RTMP, SRT, HTTP-MPEGTS) each allocate
heavyweight per-connection state — a demux pipeline, a multi-MB write/ts buffer,
2-3 goroutines, an FD — with no cap. None of the handlers authenticate, so an
unauthenticated client that knows one active stream code could open many
concurrent connections and exhaust goroutines / FDs / memory, OOM-killing the
shared process and taking every stream on the host down.

Add a connLimiter (per-stream + global) on the publisher Service and check it
BEFORE allocating any per-connection state; release on close:
- HandleMPEGTS: acquire → 503, defer release.
- HandleRTMPPlay: acquire → error, defer release.
- srtHandleSubscribe: acquire → close+return, defer release (leak-proof).
- RTSP OnPlay: acquire → 503; release in OnSessionClose via the rtspSessions
  entry, with a repeated-PLAY guard (PAUSE→PLAY can't double-count) and nil-ps
  cap-only entries handled by the touch loop / pollBytes.

Caps are configurable (MaxPlaybackConnPerStream / MaxPlaybackConnTotal): unset
applies a protective default (256 / 4096), negative means unlimited. HLS/DASH
viewers go over stateless HTTP and are not counted here.

Tests: TestConnLimiter_* (per-stream/global cap, isolation, unlimited,
no-underflow, concurrent -race balance), TestResolvePlaybackCap,
TestHandleMPEGTS_PlaybackCapRejectsAndReleases (503 over cap; slot frees after
the connection closes). The acquire/release balance across all four handlers
was reviewed for leak / double-release paths.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 55.69620% with 35 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/publisher/serve_rtsp.go 0.00% 17 Missing and 1 partial ⚠️
internal/publisher/service.go 43.75% 9 Missing ⚠️
internal/publisher/serve_rtmp.go 0.00% 4 Missing ⚠️
internal/publisher/serve_srt.go 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@ntt0601zcoder ntt0601zcoder merged commit 115a453 into main Jun 13, 2026
4 checks passed
@ntt0601zcoder ntt0601zcoder deleted the fix/playback-conn-cap branch June 13, 2026 15:42
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.

2 participants