From 77e77fb4818ecc7ca30c4203a0689a55173dcc36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 10 Jun 2026 09:23:29 +0800 Subject: [PATCH] usbip: hand the uevent netlink fd to the runtime poller The raw blocking fd had three lifecycle bugs: close() does not wake a thread blocked in recvfrom, the watchdog and the error path both closed the fd without coordination, and a closed fd number could be recycled to an unrelated connection that the lingering reader would then consume from and the second close would tear down. Wrapping the nonblocking fd in os.File gives poller-based reads that Close wakes, idempotent close, and no reuse while a read is in flight. --- service/usbip/uevent_linux.go | 42 ++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/service/usbip/uevent_linux.go b/service/usbip/uevent_linux.go index e640e8c86..3128e77e7 100644 --- a/service/usbip/uevent_linux.go +++ b/service/usbip/uevent_linux.go @@ -4,18 +4,24 @@ package usbip import ( "bytes" + "os" "golang.org/x/sys/unix" ) const ueventReceiveBufferSize = 1 << 20 +// ueventListener wraps the netlink socket in an *os.File so the fd is +// owned by the Go runtime poller: Close wakes a goroutine blocked in +// WaitUSBEvent, is idempotent, and the fd number cannot be recycled to +// another connection while a read is still in flight — all of which a +// raw fd with blocking Recvfrom gets wrong. type ueventListener struct { - fd int + file *os.File } func newUEventListener() (*ueventListener, error) { - fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM, unix.NETLINK_KOBJECT_UEVENT) + fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, unix.NETLINK_KOBJECT_UEVENT) if err != nil { return nil, err } @@ -29,23 +35,43 @@ func newUEventListener() (*ueventListener, error) { _ = unix.Close(fd) return nil, err } - return &ueventListener{fd: fd}, nil + return &ueventListener{file: os.NewFile(uintptr(fd), "netlink-uevent")}, nil } func (l *ueventListener) Close() error { - return unix.Close(l.fd) + return l.file.Close() } func (l *ueventListener) WaitUSBEvent() error { + rawConn, err := l.file.SyscallConn() + if err != nil { + return err + } var buf [16384]byte for { - n, from, err := unix.Recvfrom(l.fd, buf[:], 0) - if err == unix.ENOBUFS { - return nil - } + var ( + n int + from unix.Sockaddr + recvErr error + ) + err = rawConn.Read(func(fd uintptr) bool { + for { + n, from, recvErr = unix.Recvfrom(int(fd), buf[:], 0) + if recvErr == unix.EINTR { + continue + } + return recvErr != unix.EAGAIN + } + }) if err != nil { return err } + if recvErr == unix.ENOBUFS { + return nil + } + if recvErr != nil { + return recvErr + } if source, ok := from.(*unix.SockaddrNetlink); ok && source.Pid != 0 { continue }