From 3dea496c7f24d34488a8b9e6ec0b542548df0f3a Mon Sep 17 00:00:00 2001 From: rawdigits Date: Fri, 24 Apr 2026 21:44:37 +0000 Subject: [PATCH] overlay/tio: KeepAlive poll-path readv/writev buffers too The Poll fallback (used when IFF_VNET_HDR can't be enabled) has the same unsafe-pointer-via-uintptr pattern as Offload.rawWrite: readOne/writeOne build a [2]syscall.Iovec on the stack, pass it to syscall.Syscall as uintptr, and the kernel then DMAs in/out of the to/from slices whose Base pointers the iovec holds. Escape analysis can't see that use, so under GC pressure the backing memory could be collected or moved mid-syscall. Add runtime.KeepAlive on the iovec and the user buffer around both the SYS_READV and SYS_WRITEV syscalls. Same pattern and rationale as the prior commit on the offload path. --- overlay/tio/tio_poll_linux.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/overlay/tio/tio_poll_linux.go b/overlay/tio/tio_poll_linux.go index 4575b8d3..f2820295 100644 --- a/overlay/tio/tio_poll_linux.go +++ b/overlay/tio/tio_poll_linux.go @@ -3,6 +3,7 @@ package tio import ( "fmt" "os" + "runtime" "sync/atomic" "syscall" "unsafe" @@ -120,6 +121,13 @@ func (t *Poll) readOne(to []byte) (int, error) { } for { n, _, errno := syscall.Syscall(syscall.SYS_READV, uintptr(t.fd), uintptr(unsafe.Pointer(&iovecs[0])), 2) + // Pin the iovec + destination buffer backing array across the syscall. + // Without these the Go runtime may move/GC them while the kernel is + // still writing via DMA (we pass the iovec as uintptr, which hides it + // from escape analysis). Same class of bug as rawWrite in the Offload + // path. + runtime.KeepAlive(iovecs) + runtime.KeepAlive(to) if errno == 0 { bytesRead := int(n) if bytesRead < 4 { @@ -166,6 +174,10 @@ func (t *Poll) Write(from []byte) (int, error) { } for { n, _, errno := syscall.Syscall(syscall.SYS_WRITEV, uintptr(t.fd), uintptr(unsafe.Pointer(&iovecs[0])), 2) + // Pin the iovec + source buffer backing array across the syscall. + // See readOne's KeepAlive comment for rationale. + runtime.KeepAlive(iovecs) + runtime.KeepAlive(from) if errno == 0 { return int(n) - 4, nil }