From 2bba0a7e96c8672b66c567d44d21a312ea770d87 Mon Sep 17 00:00:00 2001 From: Eric Wendland Date: Mon, 6 Jul 2026 18:37:03 +0200 Subject: [PATCH] fix: drop malformed TUN packets Traffic control parsed packets from the full message buffer and trusted the packet's advertised length. On Windows this could panic when the TUN read returned fewer bytes than an IPv6 payload length claimed, for example slicing 2050 bytes from a 2016-byte read. Carry the actual TUN read length into TCElement.Packet, make ParsePacket reject packets whose advertised length exceeds the available bytes, and drop malformed packets instead of panicking. --- polyamide/device/send.go | 1 + polyamide/device/traffic_control.go | 3 +- polyamide/device/traffic_manip.go | 37 +++++++++++++++++++++-- polyamide/device/traffic_manip_test.go | 41 ++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 polyamide/device/traffic_manip_test.go diff --git a/polyamide/device/send.go b/polyamide/device/send.go index 1a50ee06..3f677e7c 100644 --- a/polyamide/device/send.go +++ b/polyamide/device/send.go @@ -249,6 +249,7 @@ func (device *Device) RoutineReadFromTUN() { } tce := device.GetTCElement() tce.Buffer = bufs[i] + tce.Packet = bufs[i][offset : offset+sizes[i]] tcBufs = append(tcBufs, tce) bufs[i] = device.GetMessageBuffer() diff --git a/polyamide/device/traffic_control.go b/polyamide/device/traffic_control.go index 04147042..6ecb48b9 100644 --- a/polyamide/device/traffic_control.go +++ b/polyamide/device/traffic_control.go @@ -109,8 +109,7 @@ func (device *Device) TCBatch(batch []*TCElement, tcs *TCState) { for i, elem := range batch { // process TC Filters act := TcPass - elem.ParsePacket() - if !elem.Validate() { + if !elem.ParsePacket() || !elem.Validate() { device.Log.Errorf("Found malformed packet, dropping packet") act = TcDrop } else { diff --git a/polyamide/device/traffic_manip.go b/polyamide/device/traffic_manip.go index 23061fc4..89d956eb 100644 --- a/polyamide/device/traffic_manip.go +++ b/polyamide/device/traffic_manip.go @@ -20,10 +20,25 @@ func (elem *TCElement) InitPacket(ver int, len uint16) { elem.SetLength(len) } -func (elem *TCElement) ParsePacket() { - elem.Packet = elem.Buffer[MessageTransportHeaderSize:] +func (elem *TCElement) ParsePacket() bool { + if elem == nil { + return false + } + if len(elem.Packet) == 0 { + if elem.Buffer == nil { + return false + } + elem.Packet = elem.Buffer[MessageTransportHeaderSize:] + } + if !elem.hasPacketHeader() { + return false + } l := elem.GetLength() - elem.Packet = elem.Buffer[MessageTransportHeaderSize : MessageTransportHeaderSize+l] + if int(l) > len(elem.Packet) { + return false + } + elem.Packet = elem.Packet[:l] + return true } func (elem *TCElement) Incoming() bool { @@ -137,6 +152,9 @@ func (elem *TCElement) Validate() bool { if elem == nil || len(elem.Packet) == 0 { return false } + if !elem.hasPacketHeader() { + return false + } ver := elem.GetIPVersion() if ver == 4 { if elem.GetLength() < ipv4.HeaderLen { @@ -154,6 +172,19 @@ func (elem *TCElement) Validate() bool { return true } +func (elem *TCElement) hasPacketHeader() bool { + if elem == nil || len(elem.Packet) < PolyHeaderSize { + return false + } + ver := elem.GetIPVersion() + if ver == 4 { + return len(elem.Packet) >= ipv4.HeaderLen + } else if ver == 6 { + return len(elem.Packet) >= ipv6.HeaderLen + } + return true +} + func (elem *TCElement) TTLBytes() []byte { if elem.GetIPVersion() == 4 { return elem.Packet[8:9] diff --git a/polyamide/device/traffic_manip_test.go b/polyamide/device/traffic_manip_test.go new file mode 100644 index 00000000..bf9e3ad6 --- /dev/null +++ b/polyamide/device/traffic_manip_test.go @@ -0,0 +1,41 @@ +package device + +import ( + "encoding/binary" + "testing" +) + +func TestParsePacketRejectsLengthBeyondReadBuffer(t *testing.T) { + var buf [MaxMessageSize]byte + packet := buf[MessageTransportHeaderSize : MessageTransportHeaderSize+2016] + packet[0] = 6 << 4 + binary.BigEndian.PutUint16(packet[IPv6offsetPayloadLength:IPv6offsetPayloadLength+2], 2010) + + elem := &TCElement{ + Buffer: &buf, + Packet: packet, + } + + if elem.ParsePacket() { + t.Fatal("expected oversized packet to be rejected") + } +} + +func TestParsePacketTrimsToAdvertisedLength(t *testing.T) { + var buf [MaxMessageSize]byte + packet := buf[MessageTransportHeaderSize : MessageTransportHeaderSize+128] + packet[0] = 6 << 4 + binary.BigEndian.PutUint16(packet[IPv6offsetPayloadLength:IPv6offsetPayloadLength+2], 8) + + elem := &TCElement{ + Buffer: &buf, + Packet: packet, + } + + if !elem.ParsePacket() { + t.Fatal("expected valid packet to parse") + } + if len(elem.Packet) != 48 { + t.Fatalf("expected packet length 48, got %d", len(elem.Packet)) + } +}