overlay/tio: guard Offload.Write against zero-length buffers

Write took &buf[0] before calling writeWithScratch, so the len==0 guard
in the helper could never run -- a zero-length buffer panicked on the
index instead of returning. Hoist the guard above the indexing and fold
writeWithScratch into Write since it was the only caller and duplicated
the iovec setup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
JackDoan
2026-07-27 15:37:03 -05:00
parent 35596c7708
commit 5c0a5ee4be
2 changed files with 16 additions and 10 deletions
+4 -10
View File
@@ -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) {
+12
View File
@@ -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)
}
}
}