diff --git a/internal/ingest/adapters/icecast/source.go b/internal/ingest/adapters/icecast/source.go index 2970e82..52c825f 100644 --- a/internal/ingest/adapters/icecast/source.go +++ b/internal/ingest/adapters/icecast/source.go @@ -312,12 +312,25 @@ func (s *Source) decodeWithPreference(ctx context.Context, stream io.Reader, met } } +// maxCaptureBytes caps the amount of stream data buffered while the native +// decoder is deciding whether it can handle the format. Without a cap, a +// decoder that reads extensively before returning ErrUnsupported could grow +// this buffer unboundedly on a corrupt or adversarial stream. +const maxCaptureBytes = 1 << 20 // 1 MiB + +// errCaptureLimitExceeded is returned by capturingReader when the buffer cap +// is hit. The caller should treat it like ErrUnsupported and fall back. +var errCaptureLimitExceeded = errors.New("capture buffer limit exceeded") + type capturingReader struct { r io.Reader buf bytes.Buffer } func (r *capturingReader) Read(p []byte) (int, error) { + if r.buf.Len() >= maxCaptureBytes { + return 0, errCaptureLimitExceeded + } n, err := r.r.Read(p) if n > 0 { _, _ = r.buf.Write(p[:n])