prevent decoders from returning empty NALUs (bluenviron/mediamtx#4346) (#726)

This commit is contained in:
Alessandro Ros
2025-03-22 22:45:54 +01:00
committed by GitHub
parent b6d6f6bf37
commit fa94080e84
44 changed files with 191 additions and 112 deletions

View File

@@ -60,6 +60,12 @@ func (d *Decoder) decodeOBUs(pkt *rtp.Packet) ([][]byte, error) {
return nil, fmt.Errorf("invalid header: %w", err) return nil, fmt.Errorf("invalid header: %w", err)
} }
for _, obu := range av1header.OBUElements {
if len(obu) == 0 {
return nil, fmt.Errorf("invalid OBU size")
}
}
if av1header.Z { if av1header.Z {
if d.fragmentsSize == 0 { if d.fragmentsSize == 0 {
if !d.firstPacketReceived { if !d.firstPacketReceived {

View File

@@ -295,32 +295,39 @@ func TestDecodeErrorMissingPacket(t *testing.T) {
} }
func FuzzDecoder(f *testing.F) { func FuzzDecoder(f *testing.F) {
f.Fuzz(func(_ *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{}
d.Init() //nolint:errcheck err := d.Init()
require.NoError(t, err)
d.Decode(&rtp.Packet{ //nolint:errcheck tu, err := d.Decode(&rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2,
Marker: am, Marker: am,
PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317,
SSRC: 0x9dbb7812,
}, },
Payload: a, Payload: a,
}) })
d.Decode(&rtp.Packet{ //nolint:errcheck if errors.Is(err, ErrMorePacketsNeeded) {
Header: rtp.Header{ tu, err = d.Decode(&rtp.Packet{
Version: 2, Header: rtp.Header{
Marker: bm, Marker: bm,
PayloadType: 96, SequenceNumber: 17646,
SequenceNumber: 17646, },
Timestamp: 2289527317, Payload: b,
SSRC: 0x9dbb7812, })
}, }
Payload: b,
}) if err == nil {
if len(tu) == 0 {
t.Errorf("should not happen")
}
for _, nalu := range tu {
if len(nalu) == 0 {
t.Errorf("should not happen")
}
}
}
}) })
} }

View File

@@ -2,4 +2,4 @@ go test fuzz v1
[]byte("0\x00") []byte("0\x00")
bool(true) bool(true)
[]byte("0") []byte("0")
bool(false) bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("0")
bool(false)

View File

@@ -1,5 +1,5 @@
go test fuzz v1 go test fuzz v1
[]byte("\x190") []byte("\x180")
bool(false) bool(false)
[]byte("\xd00") []byte("\xd00")
bool(false) bool(false)

View File

@@ -1,5 +1,5 @@
go test fuzz v1 go test fuzz v1
[]byte("\xd00") []byte("\xd00")
bool(false) bool(false)
[]byte("") []byte("0")
bool(false) bool(false)

View File

@@ -157,9 +157,13 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
size := uint16(payload[0])<<8 | uint16(payload[1]) size := uint16(payload[0])<<8 | uint16(payload[1])
payload = payload[2:] payload = payload[2:]
// discard padding if size == 0 {
if size == 0 && isAllZero(payload) { // discard padding
break if isAllZero(payload) {
break
}
return nil, fmt.Errorf("invalid STAP-A packet (invalid size)")
} }
if int(size) > len(payload) { if int(size) > len(payload) {

View File

@@ -275,32 +275,39 @@ func TestDecodeErrorMissingPacket(t *testing.T) {
} }
func FuzzDecoder(f *testing.F) { func FuzzDecoder(f *testing.F) {
f.Fuzz(func(_ *testing.T, a []byte, b []byte) { f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
d := &Decoder{} d := &Decoder{}
d.Init() //nolint:errcheck err := d.Init()
require.NoError(t, err)
d.Decode(&rtp.Packet{ //nolint:errcheck au, err := d.Decode(&rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Marker: am,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317,
SSRC: 0x9dbb7812,
}, },
Payload: a, Payload: a,
}) })
d.Decode(&rtp.Packet{ //nolint:errcheck if errors.Is(err, ErrMorePacketsNeeded) {
Header: rtp.Header{ au, err = d.Decode(&rtp.Packet{
Version: 2, Header: rtp.Header{
Marker: false, Marker: bm,
PayloadType: 96, SequenceNumber: 17646,
SequenceNumber: 17645, },
Timestamp: 2289527317, Payload: b,
SSRC: 0x9dbb7812, })
}, }
Payload: b,
}) if err == nil {
if len(au) == 0 {
t.Errorf("should not happen")
}
for _, nalu := range au {
if len(nalu) == 0 {
t.Errorf("should not happen")
}
}
}
}) })
} }

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("8")
bool(false)
[]byte("0")
bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("\x00\x00\x00\x01")
bool(false)

View File

@@ -1,3 +1,5 @@
go test fuzz v1 go test fuzz v1
[]byte("800") []byte("800")
bool(false)
[]byte("0") []byte("0")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("8\x00\x000")

View File

@@ -1,5 +1,5 @@
go test fuzz v1 go test fuzz v1
[]byte("") []byte("")
bool(false) bool(false)
[]byte("") []byte("0")
bool(false) bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("8\x00\x00\x00\x010")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("8")
[]byte("")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("\xdc")
bool(false)
[]byte("0")
bool(false)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("")

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("80")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("0")
bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("\xdc0")
bool(false)
[]byte("0")
bool(false)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("8\x00\x00")
bool(false)
[]byte("0")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("<")

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("\\0")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("9000")
bool(false)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("9")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("|0")
bool(true)

View File

@@ -81,7 +81,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
size := uint16(payload[0])<<8 | uint16(payload[1]) size := uint16(payload[0])<<8 | uint16(payload[1])
payload = payload[2:] payload = payload[2:]
if int(size) > len(payload) { if size == 0 || int(size) > len(payload) {
return nil, fmt.Errorf("invalid aggregation unit (invalid size)") return nil, fmt.Errorf("invalid aggregation unit (invalid size)")
} }
@@ -93,10 +93,6 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
} }
} }
if nalus == nil {
return nil, fmt.Errorf("aggregation unit doesn't contain any NALU")
}
d.firstPacketReceived = true d.firstPacketReceived = true
case h265.NALUType_FragmentationUnit: case h265.NALUType_FragmentationUnit:

View File

@@ -92,32 +92,39 @@ func TestDecodeErrorMissingPacket(t *testing.T) {
} }
func FuzzDecoder(f *testing.F) { func FuzzDecoder(f *testing.F) {
f.Fuzz(func(_ *testing.T, a []byte, b []byte) { f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
d := &Decoder{} d := &Decoder{}
d.Init() //nolint:errcheck err := d.Init()
require.NoError(t, err)
d.Decode(&rtp.Packet{ //nolint:errcheck au, err := d.Decode(&rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Marker: am,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317,
SSRC: 0x9dbb7812,
}, },
Payload: a, Payload: a,
}) })
d.Decode(&rtp.Packet{ //nolint:errcheck if errors.Is(err, ErrMorePacketsNeeded) {
Header: rtp.Header{ au, err = d.Decode(&rtp.Packet{
Version: 2, Header: rtp.Header{
Marker: false, Marker: bm,
PayloadType: 96, SequenceNumber: 17646,
SequenceNumber: 17645, },
Timestamp: 2289527317, Payload: b,
SSRC: 0x9dbb7812, })
}, }
Payload: b,
}) if err == nil {
if len(au) == 0 {
t.Errorf("should not happen")
}
for _, nalu := range au {
if len(nalu) == 0 {
t.Errorf("should not happen")
}
}
}
}) })
} }

View File

@@ -1,3 +1,5 @@
go test fuzz v1 go test fuzz v1
[]byte("0")
[]byte("b0") []byte("b0")
bool(false)
[]byte("0")
bool(false)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("a0")
bool(false)
[]byte("0")
bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("d0")
bool(false)
[]byte("0")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("00")
[]byte("b00")

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("a00")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("00")
bool(false)
[]byte("b0\xc1")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("\xe50")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("b00")
bool(false)
[]byte("0")
bool(false)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("0")
[]byte("")

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("a0")
[]byte("")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("0")
bool(false)
[]byte("0")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("b0\xd2")
[]byte("0")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("00")
bool(false)
[]byte("00")
bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("a0\x00\t000000000")
bool(false)
[]byte("b00")
bool(true)

View File

@@ -0,0 +1,5 @@
go test fuzz v1
[]byte("00")
bool(false)
[]byte("a0\x00\x00")
bool(true)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("a000")
[]byte("0")