From e9870686c8e98f10a98ea0d04787099097a454f2 Mon Sep 17 00:00:00 2001 From: JackDoan Date: Wed, 29 Jul 2026 17:14:04 -0500 Subject: [PATCH] overlay/tio: error on multi-segment WriteGSO with a bogus IP version gsoTypeFromProto returns GSO_NONE when the IP version nibble is neither 4 nor 6, so a multi-fragment superpacket went out as one silent jumbo GSO_NONE packet -- exactly the silent mis-emission the geometry checks promise not to allow. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_014ugV2edVqoz3tBvq9J6yWp --- overlay/tio/tio_gso_linux.go | 6 ++++++ overlay/tio/tun_linux_offload_test.go | 2 ++ 2 files changed, 8 insertions(+) diff --git a/overlay/tio/tio_gso_linux.go b/overlay/tio/tio_gso_linux.go index cce033ee..cdfa5d15 100644 --- a/overlay/tio/tio_gso_linux.go +++ b/overlay/tio/tio_gso_linux.go @@ -358,6 +358,12 @@ func (r *Offload) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, proto gsoType := uint8(unix.VIRTIO_NET_HDR_GSO_NONE) if len(pays) > 1 { gsoType = gsoTypeFromProto(proto, hdr[0]>>4) + if gsoType == unix.VIRTIO_NET_HDR_GSO_NONE { + // gsoTypeFromProto only yields GSO_NONE for a bogus IP version + // nibble. A multi-segment superpacket must carry a real GSO type, + // or the kernel would deliver it as a single jumbo packet. + return fmt.Errorf("tio: WriteGSO IP version %d is not GSO-capable", hdr[0]>>4) + } } var gsoSize uint16 if gsoType != unix.VIRTIO_NET_HDR_GSO_NONE { diff --git a/overlay/tio/tun_linux_offload_test.go b/overlay/tio/tun_linux_offload_test.go index 36d927ae..84d44b49 100644 --- a/overlay/tio/tun_linux_offload_test.go +++ b/overlay/tio/tun_linux_offload_test.go @@ -997,6 +997,8 @@ func TestWriteGSORejectsBadGeometry(t *testing.T) { {"undersize-middle-fragment", ipHdr, udpHdr, [][]byte{seg, make([]byte, 100), seg}, GSOProtoUDP, true}, {"oversize-last-fragment", ipHdr, tcpHdr, [][]byte{seg, make([]byte, 1201)}, GSOProtoTCP, true}, {"short-last-fragment-ok", ipHdr, udpHdr, [][]byte{seg, seg, make([]byte, 100)}, GSOProtoUDP, false}, + {"multi-segment-bad-ip-version", []byte{0x05}, udpHdr, [][]byte{seg, seg}, GSOProtoUDP, true}, + {"single-segment-bad-ip-version-ok", []byte{0x05}, udpHdr, [][]byte{seg}, GSOProtoUDP, false}, {"no-pays-noop", ipHdr, udpHdr, nil, GSOProtoUDP, false}, {"valid-udp", ipHdr, udpHdr, [][]byte{seg, seg}, GSOProtoUDP, false}, {"valid-tcp", ipHdr, tcpHdr, [][]byte{seg, seg}, GSOProtoTCP, false},