diff --git a/ebp/cablelabsebp.go b/ebp/cablelabsebp.go index b80ed29..ef67941 100644 --- a/ebp/cablelabsebp.go +++ b/ebp/cablelabsebp.go @@ -89,71 +89,89 @@ func readCableLabsEbp(data []byte) (ebp *cableLabsEbp, err error) { return nil, gots.ErrNoPayload } - index := uint8(0) + index := 0 ebp.DataFieldTag = data[index] - index += uint8(1) + index++ ebp.DataFieldLength = data[index] - index += uint8(1) + index++ // Check if the data is as advertised if ebp.DataFieldLength > 0 { if len(data) >= 7 { ebp.FormatIdentifier = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) + index += 4 ebp.DataFlags = data[index] - index += uint8(1) + index++ } else { return nil, gots.ErrInvalidEBPLength } } if ebp.ExtensionFlag() { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.ExtensionFlags = data[index] - index += uint8(1) + index++ } if ebp.SapFlag() { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.SapType = data[index] - index += uint8(1) + index++ } if ebp.GroupingFlag() { var group byte var groupExtFlag bool + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } groupExtFlag = data[index]&0x80 != 0 group = data[index] & 0x7F ebp.Grouping = append(ebp.Grouping, group) - index += uint8(1) + index++ for groupExtFlag { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } groupExtFlag = data[index]&0x80 != 0 group = data[index] & 0x7F ebp.Grouping = append(ebp.Grouping, group) - index += uint8(1) + index++ } } if ebp.TimeFlag() { + if index+8 > len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.TimeSeconds = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) + index += 4 ebp.TimeFraction = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) + index += 4 } if ebp.PartitionFlag() { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.PartitionFlags = data[index] - index += uint8(1) + index++ } - if index < ebp.DataFieldLength+2 { - if int(ebp.DataFieldLength+2) > len(data) { + if index < int(ebp.DataFieldLength)+2 { + if int(ebp.DataFieldLength)+2 > len(data) { return nil, gots.ErrInvalidEBPLength } - ebp.ReservedBytes = data[index : ebp.DataFieldLength+2] + ebp.ReservedBytes = data[index : int(ebp.DataFieldLength)+2] } // update the successful read time diff --git a/ebp/comcastebp.go b/ebp/comcastebp.go index 791acc7..22e7fd3 100644 --- a/ebp/comcastebp.go +++ b/ebp/comcastebp.go @@ -75,53 +75,65 @@ func readComcastEbp(data []byte) (ebp *comcastEbp, err error) { return nil, gots.ErrNoPayload } - index := uint8(0) + index := 0 ebp.DataFieldTag = data[index] - index += uint8(1) + index++ ebp.DataFieldLength = data[index] - index += uint8(1) + index++ // Check if the data is as advertised if ebp.DataFieldLength > 0 { if len(data) >= 3 { ebp.DataFlags = data[index] - index += uint8(1) + index++ } else { return nil, gots.ErrInvalidEBPLength } } if ebp.ExtensionFlag() { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.ExtensionFlags = data[index] - index += uint8(1) + index++ } if ebp.SapFlag() { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.SapType = data[index] - index += uint8(1) + index++ } if ebp.GroupingFlag() { + if index >= len(data) { + return nil, gots.ErrInvalidEBPLength + } group := data[index] ebp.Grouping = append(ebp.Grouping, group) - index += uint8(1) + index++ } if ebp.TimeFlag() { + if index+8 > len(data) { + return nil, gots.ErrInvalidEBPLength + } ebp.TimeSeconds = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) + index += 4 ebp.TimeFraction = binary.BigEndian.Uint32(data[index : index+4]) - index += uint8(4) + index += 4 } - if index < ebp.DataFieldLength+2 { - if int(ebp.DataFieldLength+2) > len(data) { + if index < int(ebp.DataFieldLength)+2 { + if int(ebp.DataFieldLength)+2 > len(data) { return nil, gots.ErrInvalidEBPLength } - ebp.ReservedBytes = data[index : ebp.DataFieldLength+2] + ebp.ReservedBytes = data[index : int(ebp.DataFieldLength)+2] } // update the successful read time diff --git a/ebp/truncated_test.go b/ebp/truncated_test.go new file mode 100644 index 0000000..019ffa1 --- /dev/null +++ b/ebp/truncated_test.go @@ -0,0 +1,60 @@ +/* +MIT License + +Copyright 2016 Comcast Cable Communications Management, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package ebp + +import "testing" + +// TestReadEncoderBoundaryPointTruncated verifies that a truncated EBP whose +// flag byte advertises fields that are not actually present is rejected with +// an error rather than panicking with an index/slice out of range. These +// inputs were found by fuzzing ReadEncoderBoundaryPoint. +func TestReadEncoderBoundaryPointTruncated(t *testing.T) { + cases := []struct { + name string + data []byte + }{ + // CableLabs (tag 0xDF): TimeFlag (0x08) set but the 8 time bytes are + // missing. Previously panicked in readCableLabsEbp. + {"cablelabs_time", []byte{0xDF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08}}, + // CableLabs: SapFlag (0x20) set but sap byte missing. + {"cablelabs_sap", []byte{0xDF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20}}, + // CableLabs: GroupingFlag (0x10) with the extension bit (0x80) set, + // runs off the end of the buffer in the grouping loop. + {"cablelabs_grouping", []byte{0xDF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80}}, + // Comcast (tag 0xA9): TimeFlag set but time bytes missing. + {"comcast_time", []byte{0xA9, 0x08, 0x08}}, + // Comcast: SapFlag set but sap byte missing. + {"comcast_sap", []byte{0xA9, 0x02, 0x20}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + // Must not panic; a truncated buffer is expected to error. + if _, err := ReadEncoderBoundaryPoint(tc.data); err == nil { + t.Fatalf("expected error for truncated EBP, got nil") + } + }) + } +}