mirror of
https://github.com/aler9/gortsplib
synced 2025-10-17 20:51:09 +08:00
forbid DecodeUntilMarker() from reusing buffers to avoid race conditions (#284)
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
[](https://app.codecov.io/gh/bluenviron/gortsplib/branch/main)
|
[](https://app.codecov.io/gh/bluenviron/gortsplib/branch/main)
|
||||||
[](https://pkg.go.dev/github.com/bluenviron/gortsplib/v3#pkg-index)
|
[](https://pkg.go.dev/github.com/bluenviron/gortsplib/v3#pkg-index)
|
||||||
|
|
||||||
RTSP 1.0 client and server library for the Go programming language, written for [MediaMTX](https://github.com/aler9/mediamtx).
|
RTSP 1.0 client and server library for the Go programming language, written for [MediaMTX](https://github.com/bluenviron/mediamtx).
|
||||||
|
|
||||||
Go ≥ 1.18 is required.
|
Go ≥ 1.18 is required.
|
||||||
|
|
||||||
@@ -97,7 +97,6 @@ https://pkg.go.dev/github.com/bluenviron/gortsplib/v3#pkg-index
|
|||||||
|
|
||||||
## Standards
|
## Standards
|
||||||
|
|
||||||
* [Codec standards](https://github.com/bluenviron/mediacommon#standards)
|
|
||||||
* [RFC2326, RTSP 1.0](https://datatracker.ietf.org/doc/html/rfc2326)
|
* [RFC2326, RTSP 1.0](https://datatracker.ietf.org/doc/html/rfc2326)
|
||||||
* [RFC7826, RTSP 2.0](https://datatracker.ietf.org/doc/html/rfc7826)
|
* [RFC7826, RTSP 2.0](https://datatracker.ietf.org/doc/html/rfc7826)
|
||||||
* [RFC8866, SDP: Session Description Protocol](https://datatracker.ietf.org/doc/html/rfc8866)
|
* [RFC8866, SDP: Session Description Protocol](https://datatracker.ietf.org/doc/html/rfc8866)
|
||||||
@@ -114,13 +113,14 @@ https://pkg.go.dev/github.com/bluenviron/gortsplib/v3#pkg-index
|
|||||||
* [RFC7587, RTP Payload Format for the Opus Speech and Audio Codec](https://datatracker.ietf.org/doc/html/rfc7587)
|
* [RFC7587, RTP Payload Format for the Opus Speech and Audio Codec](https://datatracker.ietf.org/doc/html/rfc7587)
|
||||||
* [RFC3640, RTP Payload Format for Transport of MPEG-4 Elementary Streams](https://datatracker.ietf.org/doc/html/rfc3640)
|
* [RFC3640, RTP Payload Format for Transport of MPEG-4 Elementary Streams](https://datatracker.ietf.org/doc/html/rfc3640)
|
||||||
* [RTP Payload Format For AV1 (v1.0)](https://aomediacodec.github.io/av1-rtp-spec/)
|
* [RTP Payload Format For AV1 (v1.0)](https://aomediacodec.github.io/av1-rtp-spec/)
|
||||||
|
* [Codec standards](https://github.com/bluenviron/mediacommon#standards)
|
||||||
* [Golang project layout](https://github.com/golang-standards/project-layout)
|
* [Golang project layout](https://github.com/golang-standards/project-layout)
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
Related projects
|
Related projects
|
||||||
|
|
||||||
* [MediaMTX](https://github.com/aler9/mediamtx)
|
* [MediaMTX](https://github.com/bluenviron/mediamtx)
|
||||||
* [pion/sdp (SDP library used internally)](https://github.com/pion/sdp)
|
* [pion/sdp (SDP library used internally)](https://github.com/pion/sdp)
|
||||||
* [pion/rtp (RTP library used internally)](https://github.com/pion/rtp)
|
* [pion/rtp (RTP library used internally)](https://github.com/pion/rtp)
|
||||||
* [pion/rtcp (RTCP library used internally)](https://github.com/pion/rtcp)
|
* [pion/rtcp (RTCP library used internally)](https://github.com/pion/rtcp)
|
||||||
|
@@ -40,7 +40,8 @@ type Decoder struct {
|
|||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
|
|
||||||
// for DecodeUntilMarker()
|
// for DecodeUntilMarker()
|
||||||
obuBuffer [][]byte
|
frameBuffer [][]byte
|
||||||
|
frameBufferLen int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
@@ -131,20 +132,27 @@ func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
l := len(obus)
|
||||||
|
|
||||||
if (len(d.obuBuffer) + len(obus)) > av1.MaxOBUsPerTemporalUnit {
|
if (d.frameBufferLen + l) > av1.MaxOBUsPerTemporalUnit {
|
||||||
return nil, 0, fmt.Errorf("OBU count (%d) exceeds maximum allowed (%d)",
|
d.frameBuffer = nil
|
||||||
len(d.obuBuffer)+len(obus), av1.MaxOBUsPerTemporalUnit)
|
d.frameBufferLen = 0
|
||||||
|
return nil, 0, fmt.Errorf("OBU count exceeds maximum allowed (%d)",
|
||||||
|
av1.MaxOBUsPerTemporalUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.obuBuffer = append(d.obuBuffer, obus...)
|
d.frameBuffer = append(d.frameBuffer, obus...)
|
||||||
|
d.frameBufferLen += l
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, 0, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := d.obuBuffer
|
ret := d.frameBuffer
|
||||||
d.obuBuffer = d.obuBuffer[:0]
|
|
||||||
|
// do not reuse frameBuffer to avoid race conditions
|
||||||
|
d.frameBuffer = nil
|
||||||
|
d.frameBufferLen = 0
|
||||||
|
|
||||||
return ret, pts, nil
|
return ret, pts, nil
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package rtpav1
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -30,6 +31,28 @@ func TestDecode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecoderErrorLimit(t *testing.T) {
|
||||||
|
d := &Decoder{}
|
||||||
|
d.Init()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; i <= av1.MaxOBUsPerTemporalUnit; i++ {
|
||||||
|
_, _, err = d.DecodeUntilMarker(&rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: 2,
|
||||||
|
Marker: false,
|
||||||
|
PayloadType: 96,
|
||||||
|
SequenceNumber: 17645,
|
||||||
|
Timestamp: 2289527317,
|
||||||
|
SSRC: 0x9dbb7812,
|
||||||
|
},
|
||||||
|
Payload: []byte{1, 2, 3, 4},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
require.EqualError(t, err, "OBU count exceeds maximum allowed (10)")
|
||||||
|
}
|
||||||
|
|
||||||
func FuzzDecoder(f *testing.F) {
|
func FuzzDecoder(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
|
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
|
||||||
d := &Decoder{}
|
d := &Decoder{}
|
||||||
|
@@ -44,7 +44,8 @@ type Decoder struct {
|
|||||||
annexBMode bool
|
annexBMode bool
|
||||||
|
|
||||||
// for DecodeUntilMarker()
|
// for DecodeUntilMarker()
|
||||||
naluBuffer [][]byte
|
frameBuffer [][]byte
|
||||||
|
frameBufferLen int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
@@ -175,20 +176,27 @@ func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
l := len(nalus)
|
||||||
|
|
||||||
if (len(d.naluBuffer) + len(nalus)) > h264.MaxNALUsPerGroup {
|
if (d.frameBufferLen + l) > h264.MaxNALUsPerGroup {
|
||||||
return nil, 0, fmt.Errorf("NALU count (%d) exceeds maximum allowed (%d)",
|
d.frameBuffer = nil
|
||||||
len(d.naluBuffer)+len(nalus), h264.MaxNALUsPerGroup)
|
d.frameBufferLen = 0
|
||||||
|
return nil, 0, fmt.Errorf("NALU count exceeds maximum allowed (%d)",
|
||||||
|
h264.MaxNALUsPerGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.naluBuffer = append(d.naluBuffer, nalus...)
|
d.frameBuffer = append(d.frameBuffer, nalus...)
|
||||||
|
d.frameBufferLen += l
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, 0, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := d.naluBuffer
|
ret := d.frameBuffer
|
||||||
d.naluBuffer = d.naluBuffer[:0]
|
|
||||||
|
// do not reuse frameBuffer to avoid race conditions
|
||||||
|
d.frameBuffer = nil
|
||||||
|
d.frameBufferLen = 0
|
||||||
|
|
||||||
return ret, pts, nil
|
return ret, pts, nil
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -183,6 +184,28 @@ func TestDecodeUntilMarker(t *testing.T) {
|
|||||||
require.Equal(t, [][]byte{{0x01, 0x02}, {0x01, 0x02}}, nalus)
|
require.Equal(t, [][]byte{{0x01, 0x02}, {0x01, 0x02}}, nalus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecoderErrorLimit(t *testing.T) {
|
||||||
|
d := &Decoder{}
|
||||||
|
d.Init()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; i <= h264.MaxNALUsPerGroup; i++ {
|
||||||
|
_, _, err = d.DecodeUntilMarker(&rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: 2,
|
||||||
|
Marker: false,
|
||||||
|
PayloadType: 96,
|
||||||
|
SequenceNumber: 17645,
|
||||||
|
Timestamp: 2289527317,
|
||||||
|
SSRC: 0x9dbb7812,
|
||||||
|
},
|
||||||
|
Payload: []byte{1, 2, 3, 4},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
require.EqualError(t, err, "NALU count exceeds maximum allowed (20)")
|
||||||
|
}
|
||||||
|
|
||||||
func FuzzDecoder(f *testing.F) {
|
func FuzzDecoder(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, a []byte, b []byte) {
|
f.Fuzz(func(t *testing.T, a []byte, b []byte) {
|
||||||
d := &Decoder{}
|
d := &Decoder{}
|
||||||
|
@@ -42,7 +42,8 @@ type Decoder struct {
|
|||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
|
|
||||||
// for DecodeUntilMarker()
|
// for DecodeUntilMarker()
|
||||||
naluBuffer [][]byte
|
frameBuffer [][]byte
|
||||||
|
frameBufferLen int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
@@ -167,20 +168,27 @@ func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
l := len(nalus)
|
||||||
|
|
||||||
if (len(d.naluBuffer) + len(nalus)) > h265.MaxNALUsPerGroup {
|
if (d.frameBufferLen + l) > h265.MaxNALUsPerGroup {
|
||||||
return nil, 0, fmt.Errorf("NALU count (%d) exceeds maximum allowed (%d)",
|
d.frameBuffer = nil
|
||||||
len(d.naluBuffer)+len(nalus), h265.MaxNALUsPerGroup)
|
d.frameBufferLen = 0
|
||||||
|
return nil, 0, fmt.Errorf("NALU count exceeds maximum allowed (%d)",
|
||||||
|
h265.MaxNALUsPerGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.naluBuffer = append(d.naluBuffer, nalus...)
|
d.frameBuffer = append(d.frameBuffer, nalus...)
|
||||||
|
d.frameBufferLen += l
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, 0, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := d.naluBuffer
|
ret := d.frameBuffer
|
||||||
d.naluBuffer = d.naluBuffer[:0]
|
|
||||||
|
// do not reuse frameBuffer to avoid race conditions
|
||||||
|
d.frameBuffer = nil
|
||||||
|
d.frameBufferLen = 0
|
||||||
|
|
||||||
return ret, pts, nil
|
return ret, pts, nil
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package rtph265
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -35,6 +36,28 @@ func TestDecode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecoderErrorLimit(t *testing.T) {
|
||||||
|
d := &Decoder{}
|
||||||
|
d.Init()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; i <= h265.MaxNALUsPerGroup; i++ {
|
||||||
|
_, _, err = d.DecodeUntilMarker(&rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: 2,
|
||||||
|
Marker: false,
|
||||||
|
PayloadType: 96,
|
||||||
|
SequenceNumber: 17645,
|
||||||
|
Timestamp: 2289527317,
|
||||||
|
SSRC: 0x9dbb7812,
|
||||||
|
},
|
||||||
|
Payload: []byte{1, 2, 3, 4},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
require.EqualError(t, err, "NALU count exceeds maximum allowed (20)")
|
||||||
|
}
|
||||||
|
|
||||||
func FuzzDecoder(f *testing.F) {
|
func FuzzDecoder(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, a []byte, b []byte) {
|
f.Fuzz(func(t *testing.T, a []byte, b []byte) {
|
||||||
d := &Decoder{}
|
d := &Decoder{}
|
||||||
|
Reference in New Issue
Block a user