mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
h264: fix crash in Annex-B decoding, add fuzz tests
This commit is contained in:
@@ -8,25 +8,38 @@ import (
|
||||
func AnnexBUnmarshal(byts []byte) ([][]byte, error) {
|
||||
bl := len(byts)
|
||||
initZeroCount := 0
|
||||
start := 0
|
||||
|
||||
outer:
|
||||
for i := 0; i < bl; i++ {
|
||||
switch byts[i] {
|
||||
case 0:
|
||||
initZeroCount++
|
||||
|
||||
case 1:
|
||||
break outer
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected byte: %d", byts[i])
|
||||
}
|
||||
}
|
||||
if initZeroCount != 2 && initZeroCount != 3 {
|
||||
for {
|
||||
if start >= bl || start >= 4 {
|
||||
return nil, fmt.Errorf("initial delimiter not found")
|
||||
}
|
||||
|
||||
start := initZeroCount + 1
|
||||
switch initZeroCount {
|
||||
case 0, 1:
|
||||
if byts[start] != 0 {
|
||||
return nil, fmt.Errorf("initial delimiter not found")
|
||||
}
|
||||
initZeroCount++
|
||||
|
||||
case 2, 3:
|
||||
switch byts[start] {
|
||||
case 1:
|
||||
start++
|
||||
break outer
|
||||
|
||||
case 0:
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("initial delimiter not found")
|
||||
}
|
||||
initZeroCount++
|
||||
}
|
||||
|
||||
start++
|
||||
}
|
||||
|
||||
zeroCount := 0
|
||||
n := 0
|
||||
|
||||
@@ -67,16 +80,15 @@ outer:
|
||||
|
||||
case 1:
|
||||
if zeroCount == 2 || zeroCount == 3 {
|
||||
if (delimStart - start) > MaxNALUSize {
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", delimStart-start, MaxNALUSize)
|
||||
l := delimStart - start
|
||||
if l == 0 {
|
||||
return nil, fmt.Errorf("invalid NALU")
|
||||
}
|
||||
if l > MaxNALUSize {
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", l, MaxNALUSize)
|
||||
}
|
||||
|
||||
nalu := byts[start:delimStart]
|
||||
if len(nalu) == 0 {
|
||||
return nil, fmt.Errorf("empty NALU")
|
||||
}
|
||||
|
||||
ret[pos] = nalu
|
||||
ret[pos] = byts[start:delimStart]
|
||||
pos++
|
||||
start = i + 1
|
||||
}
|
||||
@@ -87,15 +99,15 @@ outer:
|
||||
}
|
||||
}
|
||||
|
||||
if (bl - start) > MaxNALUSize {
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", bl-start, MaxNALUSize)
|
||||
l := bl - start
|
||||
if l == 0 {
|
||||
return nil, fmt.Errorf("invalid NALU")
|
||||
}
|
||||
if l > MaxNALUSize {
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", l, MaxNALUSize)
|
||||
}
|
||||
|
||||
nalu := byts[start:bl]
|
||||
if len(nalu) == 0 {
|
||||
return nil, fmt.Errorf("empty NALU")
|
||||
}
|
||||
ret[pos] = nalu
|
||||
ret[pos] = byts[start:bl]
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package h264
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -95,50 +94,6 @@ func TestAnnexBMarshal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnexBUnmarshalError(t *testing.T) {
|
||||
for _, ca := range []struct {
|
||||
name string
|
||||
enc []byte
|
||||
err string
|
||||
}{
|
||||
{
|
||||
"empty",
|
||||
[]byte{},
|
||||
"initial delimiter not found",
|
||||
},
|
||||
{
|
||||
"invalid initial delimiter 1",
|
||||
[]byte{0xaa, 0xbb},
|
||||
"unexpected byte: 170",
|
||||
},
|
||||
{
|
||||
"invalid initial delimiter 2",
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x01},
|
||||
"initial delimiter not found",
|
||||
},
|
||||
{
|
||||
"empty NALU 1",
|
||||
[]byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x01},
|
||||
"empty NALU",
|
||||
},
|
||||
{
|
||||
"empty NALU 2",
|
||||
[]byte{0x00, 0x00, 0x01, 0xaa, 0x00, 0x00, 0x01},
|
||||
"empty NALU",
|
||||
},
|
||||
{
|
||||
"too many nalus",
|
||||
bytes.Repeat([]byte{0x00, 0x00, 0x01, 0x0a}, 21),
|
||||
"NALU count (21) exceeds maximum allowed (20)",
|
||||
},
|
||||
} {
|
||||
t.Run(ca.name, func(t *testing.T) {
|
||||
_, err := AnnexBUnmarshal(ca.enc)
|
||||
require.EqualError(t, err, ca.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAnnexBUnmarshal(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
AnnexBUnmarshal([]byte{
|
||||
@@ -161,3 +116,9 @@ func BenchmarkAnnexBUnmarshal(b *testing.B) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzAnnexBUnmarshal(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, b []byte) {
|
||||
AnnexBUnmarshal(b)
|
||||
})
|
||||
}
|
||||
|
@@ -15,15 +15,15 @@ func AVCCUnmarshal(buf []byte) ([][]byte, error) {
|
||||
return nil, fmt.Errorf("invalid length")
|
||||
}
|
||||
|
||||
le := int(uint32(buf[pos])<<24 | uint32(buf[pos+1])<<16 | uint32(buf[pos+2])<<8 | uint32(buf[pos+3]))
|
||||
l := int(uint32(buf[pos])<<24 | uint32(buf[pos+1])<<16 | uint32(buf[pos+2])<<8 | uint32(buf[pos+3]))
|
||||
pos += 4
|
||||
|
||||
if (bl - pos) < le {
|
||||
return nil, fmt.Errorf("invalid length")
|
||||
if l == 0 {
|
||||
return nil, fmt.Errorf("invalid NALU")
|
||||
}
|
||||
|
||||
if (bl - pos) > MaxNALUSize {
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", bl-pos, MaxNALUSize)
|
||||
if l > MaxNALUSize {
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", l, MaxNALUSize)
|
||||
}
|
||||
|
||||
if (len(ret) + 1) > MaxNALUsPerGroup {
|
||||
@@ -31,8 +31,12 @@ func AVCCUnmarshal(buf []byte) ([][]byte, error) {
|
||||
len(ret)+1, MaxNALUsPerGroup)
|
||||
}
|
||||
|
||||
ret = append(ret, buf[pos:pos+le])
|
||||
pos += le
|
||||
if (bl - pos) < l {
|
||||
return nil, fmt.Errorf("invalid length")
|
||||
}
|
||||
|
||||
ret = append(ret, buf[pos:pos+l])
|
||||
pos += l
|
||||
|
||||
if (bl - pos) == 0 {
|
||||
break
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package h264
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -60,36 +59,8 @@ func TestAVCCMarshal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAVCCUnmarshalError(t *testing.T) {
|
||||
for _, ca := range []struct {
|
||||
name string
|
||||
enc []byte
|
||||
err string
|
||||
}{
|
||||
{
|
||||
"empty",
|
||||
[]byte{},
|
||||
"invalid length",
|
||||
},
|
||||
{
|
||||
"invalid length",
|
||||
[]byte{0x01},
|
||||
"invalid length",
|
||||
},
|
||||
{
|
||||
"invalid length",
|
||||
[]byte{0x00, 0x00, 0x00, 0x03},
|
||||
"invalid length",
|
||||
},
|
||||
{
|
||||
"too many nalus",
|
||||
bytes.Repeat([]byte{0x00, 0x00, 0x00, 0x01, 0x0a}, 21),
|
||||
"NALU count (21) exceeds maximum allowed (20)",
|
||||
},
|
||||
} {
|
||||
t.Run(ca.name, func(t *testing.T) {
|
||||
_, err := AVCCUnmarshal(ca.enc)
|
||||
require.EqualError(t, err, ca.err)
|
||||
func FuzzAVCCUnmarshal(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, b []byte) {
|
||||
AVCCUnmarshal(b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package h264
|
||||
|
||||
// EmulationPreventionRemove removes emlation prevention bytes from a NALU.
|
||||
// EmulationPreventionRemove removes emulation prevention bytes from a NALU.
|
||||
func EmulationPreventionRemove(nalu []byte) []byte {
|
||||
// 0x00 0x00 0x03 0x00 -> 0x00 0x00 0x00
|
||||
// 0x00 0x00 0x03 0x01 -> 0x00 0x00 0x01
|
||||
|
@@ -54,3 +54,9 @@ func TestEmulationPreventionRemove(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzEmulationPreventionRemove(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, b []byte) {
|
||||
EmulationPreventionRemove(b)
|
||||
})
|
||||
}
|
||||
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00\x00\x00")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0000")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x000\x00\x00")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00\x00\x00")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x000")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00\x01")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00\x01\x00\x00\x01")
|
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00")
|
Reference in New Issue
Block a user