Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions codec/h265parser/malformed_confrecord_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package h265parser

import (
"testing"
)

// A truncated HEVC decoder-config record (30 zero bytes: vpscount=0, spscount=0)
// drives Unmarshal past the end of the slice: after the SPS loop it does
// n++; n++; b[n] with no bounds check (the h264parser twin checks len(b) < n+1
// before reading ppscount). On the unfixed code this panics with
// index-out-of-range; with the fix it must return ErrDecconfInvalid and not panic.
func TestH265ConfRecordTruncated(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("Unmarshal panicked on truncated input (bug present): %v", r)
}
}()

b := make([]byte, 30) // >= 30 passes the only length guard; all zero
var rec AVCDecoderConfRecord
if _, err := rec.Unmarshal(b); err == nil {
t.Fatalf("expected an error on truncated input, got nil")
}
}
8 changes: 8 additions & 0 deletions codec/h265parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,10 @@ func (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
n++
n++

if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
spscount := int(b[n])
n++

Expand All @@ -588,6 +592,10 @@ func (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
n++
n++

if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
ppscount := int(b[n])
n++

Expand Down
4 changes: 4 additions & 0 deletions format/fmp4/fmp4io/avc1.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ func (a *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
4 changes: 4 additions & 0 deletions format/fmp4/fmp4io/extend.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func (a *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
8 changes: 8 additions & 0 deletions format/fmp4/fmp4io/fragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func (a *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down Expand Up @@ -499,6 +503,10 @@ func (a *TrackFrag) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
12 changes: 12 additions & 0 deletions format/fmp4/fmp4io/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ func (a *Media) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down Expand Up @@ -306,6 +310,10 @@ func (a *MediaInfo) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down Expand Up @@ -578,6 +586,10 @@ func (a *DataInfo) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
8 changes: 8 additions & 0 deletions format/fmp4/fmp4io/movie.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ func (a *Movie) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down Expand Up @@ -327,6 +331,10 @@ func (a *Track) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
4 changes: 4 additions & 0 deletions format/fmp4/fmp4io/mp4a.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ func (a *MP4ADesc) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
4 changes: 4 additions & 0 deletions format/fmp4/fmp4io/opus.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ func (a *OpusSampleEntry) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
4 changes: 4 additions & 0 deletions format/fmp4/fmp4io/refer.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ func (a *DataRefer) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
8 changes: 8 additions & 0 deletions format/fmp4/fmp4io/sampletable.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ func (a *SampleTable) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down Expand Up @@ -270,6 +274,10 @@ func (a *SampleDesc) Unmarshal(b []byte, offset int) (n int, err error) {
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if size < 8 {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
Expand Down
51 changes: 51 additions & 0 deletions format/fmp4/fmp4io/zerosize_box_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package fmp4io_test

import (
"testing"
"time"

"github.com/deepch/vdk/format/fmp4/fmp4io"
"github.com/deepch/vdk/format/mp4/mp4io"
)

// A crafted moov box whose single child declares size==0 with an unknown tag.
// The child-box loop does `n += size`; with size==0, n never advances, so the
// loop appends a Dummy to Unknowns forever and allocates without bound. The
// mp4io twin caps this (len(self.Unknowns) > 100 -> "too many unknowns"); the
// fmp4io copy had no such guard. With the size<8 guard the loop must error out
// instead of spinning.
//
// b[0:8] moov box's own header (Movie.Unmarshal just does n += 8)
// b[8:12] child size = 0x00000000 (malicious zero length)
// b[12:16] child tag = 0x00000000 (unknown -> Dummy branch)
// b[16:24] padding so that n+8 (=16) < len(b) (=24) stays true
func craftZeroSizeMoov() []byte { return make([]byte, 24) }

func returnsPromptly(t *testing.T, name string, f func() error) {
t.Helper()
done := make(chan error, 1)
go func() { done <- f() }()
select {
case err := <-done:
if err == nil {
t.Fatalf("%s: expected an error on a zero-size child box, got nil", name)
}
t.Logf("%s returned promptly, err=%v", name, err)
case <-time.After(2 * time.Second):
t.Fatalf("%s did NOT return in 2s on a zero-size child box (infinite loop / DoS)", name)
}
}

func TestFmp4MovieZeroSizeChild(t *testing.T) {
b := craftZeroSizeMoov()
returnsPromptly(t, "mp4io.Movie.Unmarshal", func() error {
var mov mp4io.Movie
_, err := mov.Unmarshal(b, 0)
return err
})
returnsPromptly(t, "fmp4io.Movie.Unmarshal", func() error {
var mov fmp4io.Movie
_, err := mov.Unmarshal(b, 0)
return err
})
}