Skip to content

Commit 38ff9db

Browse files
committed
fix: make senc parsing more robust to bad input
1 parent 9409e9b commit 38ff9db

File tree

4 files changed

+134
-37
lines changed

4 files changed

+134
-37
lines changed

mp4/fuzz_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ func FuzzDecodeBox(f *testing.F) {
5454
}
5555

5656
f.Fuzz(func(t *testing.T, b []byte) {
57-
if t.Name() == "FuzzDecodeBox/75565444c6c2f1dd" {
58-
t.Skip("There is a bug in SencBox.Size() that needs to be fixed for " + t.Name())
59-
}
60-
6157
ctx, cancel := context.WithCancel(context.Background())
6258
defer cancel()
6359
monitorMemory(ctx, t, 500*1024*1024) // 500MB

mp4/senc.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type SencBox struct {
3636
rawData []byte // intermediate storage when reading
3737
IVs []InitializationVector // 8 or 16 bytes if present
3838
SubSamples [][]SubSamplePattern
39+
readBoxSize uint64 // As read from box header
3940
}
4041

4142
// CreateSencBox - create an empty SencBox
@@ -86,25 +87,39 @@ func (s *SencBox) AddSample(sample SencSample) error {
8687

8788
// DecodeSenc - box-specific decode
8889
func DecodeSenc(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) {
90+
if hdr.Size < 16 {
91+
return nil, fmt.Errorf("box size %d less than min size 16", hdr.Size)
92+
}
8993
data, err := readBoxBody(r, hdr)
9094
if err != nil {
9195
return nil, err
9296
}
9397

9498
versionAndFlags := binary.BigEndian.Uint32(data[0:4])
99+
version := byte(versionAndFlags >> 24)
100+
flags := versionAndFlags & flagsMask
101+
if version > 0 {
102+
return nil, fmt.Errorf("version %d not supported", version)
103+
}
95104
sampleCount := binary.BigEndian.Uint32(data[4:8])
96105

97106
if len(data) < 8 {
98107
return nil, fmt.Errorf("senc: box size %d less than 16", hdr.Size)
99108
}
100109

101110
senc := SencBox{
102-
Version: byte(versionAndFlags >> 24),
111+
Version: version,
103112
rawData: data[8:], // After the first 8 bytes of box content
104-
Flags: versionAndFlags & flagsMask,
113+
Flags: flags,
105114
StartPos: startPos,
106115
SampleCount: sampleCount,
107116
readButNotParsed: true,
117+
readBoxSize: hdr.Size,
118+
}
119+
120+
if flags&UseSubSampleEncryption != 0 && (len(senc.rawData) < 2*int(sampleCount)) {
121+
return nil, fmt.Errorf("box size %d too small for %d samples and subSampleEncryption",
122+
hdr.Size, sampleCount)
108123
}
109124

110125
if senc.SampleCount == 0 || len(senc.rawData) == 0 {
@@ -116,15 +131,31 @@ func DecodeSenc(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) {
116131

117132
// DecodeSencSR - box-specific decode
118133
func DecodeSencSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, error) {
134+
if hdr.Size < 16 {
135+
return nil, fmt.Errorf("box size %d less than min size 16", hdr.Size)
136+
}
137+
119138
versionAndFlags := sr.ReadUint32()
139+
version := byte(versionAndFlags >> 24)
140+
if version > 0 {
141+
return nil, fmt.Errorf("version %d not supported", version)
142+
}
143+
flags := versionAndFlags & flagsMask
120144
sampleCount := sr.ReadUint32()
145+
146+
if flags&UseSubSampleEncryption != 0 && ((hdr.Size - 16) < 2*uint64(sampleCount)) {
147+
return nil, fmt.Errorf("box size %d too small for %d samples and subSampleEncryption",
148+
hdr.Size, sampleCount)
149+
}
150+
121151
senc := SencBox{
122-
Version: byte(versionAndFlags >> 24),
152+
Version: version,
123153
rawData: sr.ReadBytes(hdr.payloadLen() - 8), // After the first 8 bytes of box content
124-
Flags: versionAndFlags & flagsMask,
154+
Flags: flags,
125155
StartPos: startPos,
126156
SampleCount: sampleCount,
127157
readButNotParsed: true,
158+
readBoxSize: hdr.Size,
128159
}
129160

130161
if senc.SampleCount == 0 || len(senc.rawData) == 0 {
@@ -254,11 +285,14 @@ func (s *SencBox) setSubSamplesUsedFlag() {
254285

255286
// Size - box-specific type
256287
func (s *SencBox) Size() uint64 {
257-
if s.readButNotParsed {
258-
return boxHeaderSize + 8 + uint64(len(s.rawData)) // read 8 bytes after header
288+
if s.readBoxSize > 0 {
289+
return s.readBoxSize
259290
}
260-
totalSize := uint64(boxHeaderSize + 8)
291+
return s.calcSize()
292+
}
261293

294+
func (s *SencBox) calcSize() uint64 {
295+
totalSize := uint64(boxHeaderSize + 8)
262296
perSampleIVSize := uint64(s.GetPerSampleIVSize())
263297
for i := uint32(0); i < s.SampleCount; i++ {
264298
totalSize += perSampleIVSize

mp4/senc_test.go

Lines changed: 91 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,66 @@ import (
44
"bytes"
55
"testing"
66

7+
"github.com/Eyevinn/mp4ff/bits"
78
"github.com/go-test/deep"
89
)
910

1011
func TestSencDirectValues(t *testing.T) {
1112
iv8 := InitializationVector("12345678")
1213
iv16 := InitializationVector("0123456789abcdef")
13-
sencBoxes := []*SencBox{
14+
cases := []struct {
15+
desc string
16+
senc *SencBox
17+
}{
1418
{
15-
Version: 0,
16-
Flags: 0,
17-
SampleCount: 431, // No perSampleIVs
18-
perSampleIVSize: 0,
19+
desc: "No perSampleIVs",
20+
senc: &SencBox{
21+
Version: 0,
22+
Flags: 0,
23+
SampleCount: 431, // No perSampleIVs
24+
perSampleIVSize: 0,
25+
},
1926
},
2027
{
21-
Version: 0,
22-
Flags: 0,
23-
SampleCount: 1,
24-
perSampleIVSize: 8,
25-
IVs: []InitializationVector{iv8},
26-
SubSamples: [][]SubSamplePattern{{{10, 1000}}},
28+
desc: "perSampleIVSize 8",
29+
senc: &SencBox{
30+
Version: 0,
31+
Flags: 0,
32+
SampleCount: 1,
33+
perSampleIVSize: 8,
34+
IVs: []InitializationVector{iv8},
35+
SubSamples: [][]SubSamplePattern{{{10, 1000}}},
36+
},
2737
},
2838
{
29-
Version: 0,
30-
Flags: 0,
31-
SampleCount: 1,
32-
perSampleIVSize: 16,
33-
IVs: []InitializationVector{iv16},
34-
SubSamples: [][]SubSamplePattern{{{10, 1000}, {20, 2000}}},
39+
desc: "perSampleIVSize 16",
40+
senc: &SencBox{
41+
Version: 0,
42+
Flags: 0,
43+
SampleCount: 1,
44+
perSampleIVSize: 16,
45+
IVs: []InitializationVector{iv16},
46+
SubSamples: [][]SubSamplePattern{{{10, 1000}, {20, 2000}}},
47+
},
3548
},
3649
{
37-
Version: 0,
38-
Flags: 0,
39-
SampleCount: 2,
40-
perSampleIVSize: 16,
41-
IVs: []InitializationVector{iv16, iv16},
42-
SubSamples: [][]SubSamplePattern{{{10, 1000}}, {{20, 2000}}},
50+
desc: "perSampleIVSize 16, 2 subsamples",
51+
senc: &SencBox{
52+
Version: 0,
53+
Flags: 0,
54+
SampleCount: 2,
55+
perSampleIVSize: 16,
56+
IVs: []InitializationVector{iv16, iv16},
57+
SubSamples: [][]SubSamplePattern{{{10, 1000}}, {{20, 2000}}},
58+
},
4359
},
4460
}
4561

46-
for _, senc := range sencBoxes {
47-
sencDiffAfterEncodeAndDecode(t, senc, 0)
48-
sencDiffAfterEncodeAndDecode(t, senc, senc.perSampleIVSize)
62+
for _, c := range cases {
63+
t.Run(c.desc, func(t *testing.T) {
64+
sencDiffAfterEncodeAndDecode(t, c.senc, 0)
65+
sencDiffAfterEncodeAndDecode(t, c.senc, c.senc.perSampleIVSize)
66+
})
4967
}
5068
}
5169

@@ -147,3 +165,50 @@ func TestImplicitIVSize(t *testing.T) {
147165
}
148166
}
149167
}
168+
169+
func TestBadSencData(t *testing.T) {
170+
// raw senc box with version > 2 */
171+
cases := []struct {
172+
desc string
173+
raw []byte
174+
err string
175+
}{
176+
{
177+
desc: "too short",
178+
raw: []byte{0x00, 0x00, 0x00, 0x0f, 's', 'e', 'n', 'c', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
179+
err: "decode senc pos 0: box size 15 less than min size 16",
180+
},
181+
{
182+
desc: "v1 not supported",
183+
raw: []byte{0x00, 0x00, 0x00, 0x10, 's', 'e', 'n', 'c', 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
184+
err: "decode senc pos 0: version 1 not supported",
185+
},
186+
{
187+
desc: "too short for subsample encryption",
188+
raw: []byte{0x00, 0x00, 0x00, 0x10, 's', 'e', 'n', 'c', 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff},
189+
err: "decode senc pos 0: box size 16 too small for 255 samples and subSampleEncryption",
190+
},
191+
}
192+
193+
for _, c := range cases {
194+
t.Run(c.desc, func(t *testing.T) {
195+
buf := bytes.NewBuffer(c.raw)
196+
_, err := DecodeBox(0, buf)
197+
if err == nil {
198+
t.Errorf("expected error %q, but got nil", c.err)
199+
}
200+
if err.Error() != c.err {
201+
t.Errorf("expected error %q, got %q", c.err, err.Error())
202+
}
203+
204+
sr := bits.NewFixedSliceReader(c.raw)
205+
_, err = DecodeBoxSR(0, sr)
206+
if err == nil {
207+
t.Errorf("expected error %q, but got nil", c.err)
208+
}
209+
if err.Error() != c.err {
210+
t.Errorf("expected error %q, got %q", c.err, err.Error())
211+
}
212+
})
213+
}
214+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
go test fuzz v1
2+
[]byte("\x00\x00\x00\x10senc\x00000\xfd\xfd\xfd\xbc")

0 commit comments

Comments
 (0)