diff --git a/overlay/tio/tio_gso_linux.go b/overlay/tio/tio_gso_linux.go index 37a2f9f2..64ca75e6 100644 --- a/overlay/tio/tio_gso_linux.go +++ b/overlay/tio/tio_gso_linux.go @@ -276,22 +276,16 @@ func (r *Offload) decodeRead(pktLen int) error { } func (r *Offload) Write(buf []byte) (int, error) { + if len(buf) == 0 { + return 0, nil + } iovs := [2]unix.Iovec{ {Base: &validVnetHdr[0]}, {Base: &buf[0]}, } iovs[0].SetLen(virtio.Size) iovs[1].SetLen(len(buf)) - return r.writeWithScratch(buf, &iovs) -} - -func (r *Offload) writeWithScratch(buf []byte, iovs *[2]unix.Iovec) (int, error) { - if len(buf) == 0 { - return 0, nil - } - iovs[1].Base = &buf[0] - iovs[1].SetLen(len(buf)) - return r.rawWrite(unsafe.Slice(&iovs[0], len(iovs))) + return r.rawWrite(unsafe.Slice(&iovs[0], 2)) } func (r *Offload) rawWrite(iovs []unix.Iovec) (int, error) { diff --git a/overlay/tio/tun_linux_offload_test.go b/overlay/tio/tun_linux_offload_test.go index d2b69fae..59d88cca 100644 --- a/overlay/tio/tun_linux_offload_test.go +++ b/overlay/tio/tun_linux_offload_test.go @@ -854,3 +854,15 @@ func TestDecodeReadFitsMaxTSOAtDrainThreshold(t *testing.T) { t.Fatalf("got %d segments, want %d", gotSegs, wantSegs) } } + +// TestOffloadWriteZeroLength: a zero-length Write must be a no-op, not a +// panic. The guard used to live below the &buf[0] that tripped on it. +func TestOffloadWriteZeroLength(t *testing.T) { + tf := &Offload{fd: -1} // any write reaching the fd would fail loudly + for _, buf := range [][]byte{nil, {}} { + n, err := tf.Write(buf) + if n != 0 || err != nil { + t.Errorf("Write(len=0) = (%d, %v), want (0, nil)", n, err) + } + } +}