mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 07:06:59 +02:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dea496c7f | |||
| 7c38aa7e6b |
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
@@ -263,12 +264,24 @@ func (r *Offload) writeWithScratch(buf []byte, iovs *[2]unix.Iovec) (int, error)
|
||||
// to validVnetHdr during Offload construction so we don't rebuild it here.
|
||||
iovs[1].Base = &buf[0]
|
||||
iovs[1].SetLen(len(buf))
|
||||
return r.rawWrite(unsafe.Slice(&iovs[0], len(iovs)))
|
||||
iovPtr := unsafe.Pointer(&iovs[0])
|
||||
// Pin the caller's buffer AND the iovec array through the syscall.
|
||||
return r.rawWrite(iovPtr, 2, buf, iovs)
|
||||
}
|
||||
|
||||
func (r *Offload) rawWrite(iovs []unix.Iovec) (int, error) {
|
||||
func (r *Offload) rawWrite(iovs unsafe.Pointer, iovcnt int, keepAlive ...interface{}) (int, error) {
|
||||
for {
|
||||
n, _, errno := syscall.Syscall(unix.SYS_WRITEV, uintptr(r.fd), uintptr(unsafe.Pointer(&iovs[0])), uintptr(len(iovs)))
|
||||
n, _, errno := syscall.Syscall(unix.SYS_WRITEV, uintptr(r.fd), uintptr(iovs), uintptr(iovcnt))
|
||||
// Anchor the iovec array + every user-supplied payload slice
|
||||
// through the syscall return. Without these, Go's GC may move or
|
||||
// collect the underlying backing arrays while the kernel is still
|
||||
// reading them via DMA (we pass the iovec as uintptr, so the
|
||||
// compiler does not keep it live). Observed in practice as a
|
||||
// kernel refcount underflow on tun_chr_write_iter / sock_wfree.
|
||||
runtime.KeepAlive(iovs)
|
||||
for _, ka := range keepAlive {
|
||||
runtime.KeepAlive(ka)
|
||||
}
|
||||
if errno == 0 {
|
||||
if int(n) < virtioNetHdrLen {
|
||||
return 0, io.ErrShortWrite
|
||||
@@ -351,7 +364,13 @@ func (r *Offload) WriteGSO(hdr []byte, pays [][]byte, gsoSize uint16, isV6 bool,
|
||||
r.gsoIovs[2+i].SetLen(len(p))
|
||||
}
|
||||
|
||||
_, err := r.rawWrite(r.gsoIovs)
|
||||
iovPtr := unsafe.Pointer(&r.gsoIovs[0])
|
||||
iovCnt := len(r.gsoIovs)
|
||||
// Pin EVERYTHING the kernel might still read via DMA: the backing iovec
|
||||
// slice, the IP/TCP header buffer, and every individual payload
|
||||
// fragment. Skipping any of these risks a use-after-free in
|
||||
// tun_chr_write_iter if GC runs mid-syscall.
|
||||
_, err := r.rawWrite(iovPtr, iovCnt, r.gsoIovs, hdr, pays)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ func newTun(c *config.C, l *logrus.Logger, vpnNetworks []netip.Prefix, multiqueu
|
||||
return nil, err
|
||||
}
|
||||
vnetHdr := true
|
||||
name, err := tunSetIff(fd, nameStr, baseFlags|unix.IFF_VNET_HDR)
|
||||
name, err := tunSetIff(fd, nameStr, baseFlags|unix.IFF_VNET_HDR|unix.IFF_NAPI)
|
||||
if err != nil {
|
||||
_ = unix.Close(fd)
|
||||
vnetHdr = false
|
||||
@@ -307,7 +307,7 @@ func (t *tun) NewMultiQueueReader() error {
|
||||
|
||||
flags := uint16(unix.IFF_TUN | unix.IFF_NO_PI | unix.IFF_MULTI_QUEUE)
|
||||
if t.vnetHdr {
|
||||
flags |= unix.IFF_VNET_HDR
|
||||
flags |= unix.IFF_VNET_HDR | unix.IFF_NAPI
|
||||
}
|
||||
if _, err = tunSetIff(fd, t.Device, flags); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
|
||||
Reference in New Issue
Block a user