diff --git a/CHANGELOG.md b/CHANGELOG.md index c022b993..e4391923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `avc.ParseSPSNALUnit` rejects an out-of-range + `num_ref_frames_in_pic_order_cnt_cycle` (valid range 0-255) instead of + allocating a slice sized from the raw field, which let a tiny malformed SPS + NAL unit trigger a multi-gigabyte allocation (found by fuzzing) - `MdatBox.HeaderSize` accounts for large lazy payloads, so trun data offsets are no longer 8 bytes short for fragments above 4 GiB diff --git a/avc/sps.go b/avc/sps.go index 3805bf28..bef71acd 100644 --- a/avc/sps.go +++ b/avc/sps.go @@ -181,6 +181,13 @@ func ParseSPSNALUnit(data []byte, parseVUIBeyondAspectRatio bool) (*SPS, error) sps.OffsetForNonRefPic = reader.ReadExpGolomb() sps.OffsetForTopToBottomField = reader.ReadExpGolomb() numRefFramesInPicOrderCntCycle := reader.ReadExpGolomb() + // num_ref_frames_in_pic_order_cnt_cycle shall be in the range of 0 to 255 + // (ISO/IEC 14496-10 Section 7.4.2.1.1). Guard against a bogus value before + // allocating, to avoid gigabytes being reserved from a tiny malformed NAL unit. + if numRefFramesInPicOrderCntCycle > 255 { + reader.SetError(fmt.Errorf("num_ref_frames_in_pic_order_cnt_cycle %d out of range [0, 255]", numRefFramesInPicOrderCntCycle)) + return nil, reader.AccError() + } sps.RefFramesInPicOrderCntCycle = make([]uint, numRefFramesInPicOrderCntCycle) for i := 0; i < int(numRefFramesInPicOrderCntCycle); i++ { sps.RefFramesInPicOrderCntCycle[i] = reader.ReadExpGolomb() diff --git a/avc/sps_test.go b/avc/sps_test.go index 71297b5d..e0784cfb 100644 --- a/avc/sps_test.go +++ b/avc/sps_test.go @@ -325,3 +325,20 @@ func TestCodecString(t *testing.T) { t.Errorf("expected codec: %q, got %q", expected, codec) } } + +// TestSPSParserNumRefFramesInPicOrderCntCycle verifies that an out-of-range +// num_ref_frames_in_pic_order_cnt_cycle is rejected instead of triggering a +// multi-gigabyte allocation. The NAL unit below sets pic_order_cnt_type = 1 and +// encodes a ~1 billion value for num_ref_frames_in_pic_order_cnt_cycle; before +// the bound check this caused ParseSPSNALUnit to allocate gigabytes from 22 +// bytes of input. Found by fuzzing. +func TestSPSParserNumRefFramesInPicOrderCntCycle(t *testing.T) { + byteData, _ := hex.DecodeString("27303030032527000000024242311f30313030303030") + sps, err := avc.ParseSPSNALUnit(byteData, true) + if err == nil { + t.Error("expected an error for out-of-range num_ref_frames_in_pic_order_cnt_cycle") + } + if sps != nil { + t.Errorf("expected nil SPS on error, got %+v", sps) + } +}