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) {
|
func AnnexBUnmarshal(byts []byte) ([][]byte, error) {
|
||||||
bl := len(byts)
|
bl := len(byts)
|
||||||
initZeroCount := 0
|
initZeroCount := 0
|
||||||
|
start := 0
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for i := 0; i < bl; i++ {
|
for {
|
||||||
switch byts[i] {
|
if start >= bl || start >= 4 {
|
||||||
case 0:
|
return nil, fmt.Errorf("initial delimiter not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch initZeroCount {
|
||||||
|
case 0, 1:
|
||||||
|
if byts[start] != 0 {
|
||||||
|
return nil, fmt.Errorf("initial delimiter not found")
|
||||||
|
}
|
||||||
initZeroCount++
|
initZeroCount++
|
||||||
|
|
||||||
case 1:
|
case 2, 3:
|
||||||
break outer
|
switch byts[start] {
|
||||||
|
case 1:
|
||||||
|
start++
|
||||||
|
break outer
|
||||||
|
|
||||||
default:
|
case 0:
|
||||||
return nil, fmt.Errorf("unexpected byte: %d", byts[i])
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("initial delimiter not found")
|
||||||
|
}
|
||||||
|
initZeroCount++
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if initZeroCount != 2 && initZeroCount != 3 {
|
start++
|
||||||
return nil, fmt.Errorf("initial delimiter not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start := initZeroCount + 1
|
|
||||||
zeroCount := 0
|
zeroCount := 0
|
||||||
n := 0
|
n := 0
|
||||||
|
|
||||||
@@ -67,16 +80,15 @@ outer:
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
if zeroCount == 2 || zeroCount == 3 {
|
if zeroCount == 2 || zeroCount == 3 {
|
||||||
if (delimStart - start) > MaxNALUSize {
|
l := delimStart - start
|
||||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", delimStart-start, MaxNALUSize)
|
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]
|
ret[pos] = byts[start:delimStart]
|
||||||
if len(nalu) == 0 {
|
|
||||||
return nil, fmt.Errorf("empty NALU")
|
|
||||||
}
|
|
||||||
|
|
||||||
ret[pos] = nalu
|
|
||||||
pos++
|
pos++
|
||||||
start = i + 1
|
start = i + 1
|
||||||
}
|
}
|
||||||
@@ -87,15 +99,15 @@ outer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bl - start) > MaxNALUSize {
|
l := bl - start
|
||||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", bl-start, MaxNALUSize)
|
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]
|
ret[pos] = byts[start:bl]
|
||||||
if len(nalu) == 0 {
|
|
||||||
return nil, fmt.Errorf("empty NALU")
|
|
||||||
}
|
|
||||||
ret[pos] = nalu
|
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package h264
|
package h264
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func BenchmarkAnnexBUnmarshal(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
AnnexBUnmarshal([]byte{
|
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")
|
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
|
pos += 4
|
||||||
|
|
||||||
if (bl - pos) < le {
|
if l == 0 {
|
||||||
return nil, fmt.Errorf("invalid length")
|
return nil, fmt.Errorf("invalid NALU")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bl - pos) > MaxNALUSize {
|
if l > MaxNALUSize {
|
||||||
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", bl-pos, MaxNALUSize)
|
return nil, fmt.Errorf("NALU size (%d) is too big (maximum is %d)", l, MaxNALUSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len(ret) + 1) > MaxNALUsPerGroup {
|
if (len(ret) + 1) > MaxNALUsPerGroup {
|
||||||
@@ -31,8 +31,12 @@ func AVCCUnmarshal(buf []byte) ([][]byte, error) {
|
|||||||
len(ret)+1, MaxNALUsPerGroup)
|
len(ret)+1, MaxNALUsPerGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = append(ret, buf[pos:pos+le])
|
if (bl - pos) < l {
|
||||||
pos += le
|
return nil, fmt.Errorf("invalid length")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, buf[pos:pos+l])
|
||||||
|
pos += l
|
||||||
|
|
||||||
if (bl - pos) == 0 {
|
if (bl - pos) == 0 {
|
||||||
break
|
break
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package h264
|
package h264
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -60,36 +59,8 @@ func TestAVCCMarshal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAVCCUnmarshalError(t *testing.T) {
|
func FuzzAVCCUnmarshal(f *testing.F) {
|
||||||
for _, ca := range []struct {
|
f.Fuzz(func(t *testing.T, b []byte) {
|
||||||
name string
|
AVCCUnmarshal(b)
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package h264
|
package h264
|
||||||
|
|
||||||
// EmulationPreventionRemove removes emlation prevention bytes from a NALU.
|
// EmulationPreventionRemove removes emulation prevention bytes from a NALU.
|
||||||
func EmulationPreventionRemove(nalu []byte) []byte {
|
func EmulationPreventionRemove(nalu []byte) []byte {
|
||||||
// 0x00 0x00 0x03 0x00 -> 0x00 0x00 0x00
|
// 0x00 0x00 0x03 0x00 -> 0x00 0x00 0x00
|
||||||
// 0x00 0x00 0x03 0x01 -> 0x00 0x00 0x01
|
// 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