From 15610c9988e62711fd371f2f3e6e47ac78063947 Mon Sep 17 00:00:00 2001 From: sirzooro Date: Sun, 21 Jun 2026 14:59:58 +0200 Subject: [PATCH] Check data length it RTP ExtHdr handlers Added data length checks to OneByteHeaderExtension, TwoByteHeaderExtension and RawExtension to prevent panics. They are probably not used by pion itself, so fix is just in case someone else still uses them. --- header_extension.go | 87 ++++++++++++++++++++++++++++++++++++++++ header_extension_test.go | 3 +- 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/header_extension.go b/header_extension.go index 174bd4e..5ed654b 100644 --- a/header_extension.go +++ b/header_extension.go @@ -10,6 +10,18 @@ import ( "io" ) +/* + RFC 3550, RTP Header Extension: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | defined by profile | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | header extension | + | .... | +*/ + const ( headerExtensionIDReserved = 0xF ) @@ -49,6 +61,10 @@ func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error { payloadLen := int(e.payload[n]&^0xF0 + 1) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], append(buf, e.payload[n+payloadLen:]...)...) @@ -70,6 +86,9 @@ func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error { // GetIDs returns the available IDs. func (e *OneByteHeaderExtension) GetIDs() []uint8 { + if len(e.payload) < 4 { + return nil + } ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4])) for n := 4; n < len(e.payload); { if e.payload[n] == 0x00 { // padding @@ -86,6 +105,10 @@ func (e *OneByteHeaderExtension) GetIDs() []uint8 { break } + if n+payloadLen > len(e.payload) { + break + } + ids = append(ids, extid) n += payloadLen } @@ -106,6 +129,10 @@ func (e *OneByteHeaderExtension) Get(id uint8) []byte { payloadLen := int(e.payload[n]&^0xF0 + 1) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { return e.payload[n : n+payloadLen] } @@ -127,6 +154,10 @@ func (e *OneByteHeaderExtension) Del(id uint8) error { extid := e.payload[n] >> 4 payloadLen := int(e.payload[n]&^0xF0 + 1) + if n+1+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], e.payload[n+1+payloadLen:]...) @@ -140,10 +171,17 @@ func (e *OneByteHeaderExtension) Del(id uint8) error { // Unmarshal parses the extension payload. func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) { + if len(buf) < 4 { + return 0, errTooSmall + } profile := binary.BigEndian.Uint16(buf[0:2]) if profile != ExtensionProfileOneByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } + extLen := binary.BigEndian.Uint16(buf[2:4]) + if len(buf) < 4+int(extLen)*4 { + return 0, errTooSmall + } e.payload = buf return len(buf), nil @@ -190,9 +228,17 @@ func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error { extid := e.payload[n] n++ + if n >= len(e.payload) { + break + } + payloadLen := int(e.payload[n]) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], append(buf, e.payload[n+payloadLen:]...)...) @@ -214,6 +260,9 @@ func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error { // GetIDs returns the available IDs. func (e *TwoByteHeaderExtension) GetIDs() []uint8 { + if len(e.payload) < 4 { + return nil + } ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4])) for n := 4; n < len(e.payload); { if e.payload[n] == 0x00 { // padding @@ -225,9 +274,17 @@ func (e *TwoByteHeaderExtension) GetIDs() []uint8 { extid := e.payload[n] n++ + if n >= len(e.payload) { + break + } + payloadLen := int(e.payload[n]) n++ + if n+payloadLen > len(e.payload) { + break + } + ids = append(ids, extid) n += payloadLen } @@ -247,9 +304,17 @@ func (e *TwoByteHeaderExtension) Get(id uint8) []byte { extid := e.payload[n] n++ + if n >= len(e.payload) { + break + } + payloadLen := int(e.payload[n]) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { return e.payload[n : n+payloadLen] } @@ -270,8 +335,16 @@ func (e *TwoByteHeaderExtension) Del(id uint8) error { extid := e.payload[n] + if n+1 >= len(e.payload) { + break + } + payloadLen := int(e.payload[n+1]) + if n+2+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], e.payload[n+2+payloadLen:]...) @@ -285,10 +358,17 @@ func (e *TwoByteHeaderExtension) Del(id uint8) error { // Unmarshal parses the extension payload. func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) { + if len(buf) < 4 { + return 0, errTooSmall + } profile := binary.BigEndian.Uint16(buf[0:2]) if profile != ExtensionProfileTwoByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } + extLen := binary.BigEndian.Uint16(buf[2:4]) + if len(buf) < 4+int(extLen)*4 { + return 0, errTooSmall + } e.payload = buf return len(buf), nil @@ -357,10 +437,17 @@ func (e *RawExtension) Del(id uint8) error { // Unmarshal parses the extension from the given buffer. func (e *RawExtension) Unmarshal(buf []byte) (int, error) { + if len(buf) < 4 { + return 0, errTooSmall + } profile := binary.BigEndian.Uint16(buf[0:2]) if profile == ExtensionProfileOneByte || profile == ExtensionProfileTwoByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } + extLen := binary.BigEndian.Uint16(buf[2:4]) + if len(buf) < 4+int(extLen)*4 { + return 0, errTooSmall + } e.payload = buf return len(buf), nil diff --git a/header_extension_test.go b/header_extension_test.go index d7a228b..575862d 100644 --- a/header_extension_test.go +++ b/header_extension_test.go @@ -303,7 +303,8 @@ func TestHeaderExtension_RFC8285TwoByteExtensionRewrite(t *testing.T) { func TestHeaderExtension_Raw(t *testing.T) { ext := &RawExtension{} - expectedPayload := []byte{0xBE, 0xEF} + // 4-byte raw extension: profile=0xBEEF (not one-byte/two-byte), length=0 words + expectedPayload := []byte{0xBE, 0xEF, 0x00, 0x00} assert.Error(t, ext.Set(5, expectedPayload)) assert.NoError(t, ext.Set(0, expectedPayload))