From 07cb8ad1a427c98005b93747a5baf64bb4889489 Mon Sep 17 00:00:00 2001 From: "Chris (ChrisJr404)" <11917633+ChrisJr404@users.noreply.github.com> Date: Tue, 18 Aug 2026 03:29:49 -0400 Subject: [PATCH] scte35, psi: guard bounds before trusting pointer/length fields NewSCTE35 and NewPMT both index into the input before checking it's long enough. Empty input panics in psi.PointerField (reads data[0]), and a PMT section length larger than the buffer slices past the end. Bounds-check first and return the existing error types instead. --- psi/pmt.go | 16 ++++++++++++++-- psi/pmt_test.go | 17 +++++++++++++++++ scte35/scte35.go | 5 +++++ scte35/scte35_test.go | 19 ++++++++++++++++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/psi/pmt.go b/psi/pmt.go index c68fb28..6d87bf7 100644 --- a/psi/pmt.go +++ b/psi/pmt.go @@ -103,10 +103,20 @@ func NewPMT(pmtBytes []byte) (PMT, error) { } func (p *pmt) parseTables(pmtBytes []byte) error { - sectionBytes := pmtBytes[1+PointerField(pmtBytes):] + if len(pmtBytes) < 1 { + return gots.ErrShortPayload + } + start := 1 + int(PointerField(pmtBytes)) + if len(pmtBytes) < start { + return gots.ErrShortPayload + } + sectionBytes := pmtBytes[start:] for len(sectionBytes) > 2 && sectionBytes[0] != 0xFF { tableLength := sectionLength(sectionBytes) + if len(sectionBytes) < int(tableLength)+3 { + return gots.ErrShortPayload + } if tableID(sectionBytes) == 0x2 { err := p.parsePMTSection(sectionBytes[0 : 3+tableLength]) @@ -277,7 +287,9 @@ func CanBuildPMT(payload []byte, sectionLength uint16) bool { // FilterPMTPacketsToPids filters the PMT contents of the provided packet to the PIDs provided and returns a new packet(s). // For example: if the provided PMT has PIDs 101, 102, and 103 and the provided PIDs are 101 and 102, -// the new PMT will have only descriptors for PID 101 and 102. The descriptor for PID 103 will be stripped from the new PMT packet. +// +// the new PMT will have only descriptors for PID 101 and 102. The descriptor for PID 103 will be stripped from the new PMT packet. +// // Returns packets and nil error if all pids are present in the PMT. // Returns packets and non-nil error if some pids are present in the PMT. // Returns nil packets and non-nil error if none of the pids are present in the PMT. diff --git a/psi/pmt_test.go b/psi/pmt_test.go index 5080ccc..648fec5 100644 --- a/psi/pmt_test.go +++ b/psi/pmt_test.go @@ -932,3 +932,20 @@ func TestIsDolbyATMOS(t *testing.T) { t.Errorf("Positive Dolby ATMOS Stream failed. Supposed to be a Dolby ATMOS stream.") } } + +// TestNewPMTTruncated makes sure malformed/truncated input returns an error +// instead of panicking. A too-long section length used to slice past the end of +// the buffer, and empty input panicked in PointerField. +func TestNewPMTTruncated(t *testing.T) { + cases := [][]byte{ + {}, // empty, panicked in PointerField + {0x20}, // pointer field points past the buffer + {0x00, 0x02, 0x0F, 0xFF}, // section length larger than the buffer + } + for i, data := range cases { + _, err := NewPMT(data) + if err != gots.ErrShortPayload { + t.Errorf("case %d (%v): expected ErrShortPayload, got %v", i, data, err) + } + } +} diff --git a/scte35/scte35.go b/scte35/scte35.go index 7b9ba16..2cc1040 100644 --- a/scte35/scte35.go +++ b/scte35/scte35.go @@ -82,6 +82,11 @@ func (s *scte35) parseTable(data []byte) error { b, _ := buf.ReadByte() return b } + // PointerField reads data[0], so we need at least one byte before the + // length check below can trust it. + if len(data) < 1 { + return gots.ErrInvalidSCTE35Length + } if buf.Len() < int(uint16(psi.PointerField(data))+psi.PSIHeaderLen+15) { return gots.ErrInvalidSCTE35Length } diff --git a/scte35/scte35_test.go b/scte35/scte35_test.go index 745b4d9..48ccca5 100644 --- a/scte35/scte35_test.go +++ b/scte35/scte35_test.go @@ -1,7 +1,7 @@ /* MIT License -Copyright 2016 Comcast Cable Communications Management, LLC +# 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 @@ -408,3 +408,20 @@ func TestParseSegmentationDescriptor_Segments(t *testing.T) { t.Errorf("want segment_num %d, got %d", want, got) } } + +// TestNewSCTE35Truncated makes sure malformed/truncated input returns an error +// instead of panicking. Empty input used to panic in psi.PointerField, which +// reads data[0] before the length check ran. +func TestNewSCTE35Truncated(t *testing.T) { + cases := [][]byte{ + {}, // empty, panicked in PointerField + {0x00}, // pointer field only + {0x00, 0x02}, + } + for i, data := range cases { + _, err := NewSCTE35(data) + if err != gots.ErrInvalidSCTE35Length { + t.Errorf("case %d (%v): expected ErrInvalidSCTE35Length, got %v", i, data, err) + } + } +}