From 1de46564a766f9647b22ebab0f35bccd14291460 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Thu, 8 Feb 2024 08:46:24 +0100 Subject: [PATCH] runtime: make netpoll events sources identifiable on Windows This is another attempt at CL 558895, but without adding stale pollDescs protection, which deviates from the original goal of the CL and adds complexity without proper testing. It is currently not possible to distinguish between a netpollBreak, an internal/poll WSA operation, and an external WSA operation (as in #58870). This can cause spurious wakeups when external WSA operations are retrieved from the queue, as they are treated as netpollBreak events. This CL makes use of completion keys to identify the source of the event. While here, fix TestWSASocketConflict, which was not properly exercising the "external WSA operation" case. Change-Id: I91f746d300d32eb7fed3c8f27266fef379360d98 Reviewed-on: https://go-review.googlesource.com/c/go/+/561895 Reviewed-by: Than McIntosh LUCI-TryBot-Result: Go LUCI Reviewed-by: David Chase Reviewed-by: Alex Brainman --- src/internal/poll/fd_windows_test.go | 25 ++----- src/runtime/netpoll_windows.go | 101 +++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/internal/poll/fd_windows_test.go b/src/internal/poll/fd_windows_test.go index 1cee18dcba..8bf92be7c3 100644 --- a/src/internal/poll/fd_windows_test.go +++ b/src/internal/poll/fd_windows_test.go @@ -133,23 +133,14 @@ func TestWSASocketConflict(t *testing.T) { var outbuf _TCP_INFO_v0 cbbr := uint32(0) - var ovs []syscall.Overlapped = make([]syscall.Overlapped, 2) - // Attempt to exercise behavior where a user-owned syscall.Overlapped - // induces an invalid pointer dereference in the Windows-specific version - // of runtime.netpoll. - ovs[1].Internal -= 1 - + var ov syscall.Overlapped // Create an event so that we can efficiently wait for completion // of a requested overlapped I/O operation. - ovs[0].HEvent, _ = windows.CreateEvent(nil, 0, 0, nil) - if ovs[0].HEvent == 0 { + ov.HEvent, _ = windows.CreateEvent(nil, 0, 0, nil) + if ov.HEvent == 0 { t.Fatalf("could not create the event!") } - - // Set the low bit of the Event Handle so that the completion - // of the overlapped I/O event will not trigger a completion event - // on any I/O completion port associated with the handle. - ovs[0].HEvent |= 0x1 + defer syscall.CloseHandle(ov.HEvent) if err = fd.WSAIoctl( SIO_TCP_INFO, @@ -158,7 +149,7 @@ func TestWSASocketConflict(t *testing.T) { (*byte)(unsafe.Pointer(&outbuf)), uint32(unsafe.Sizeof(outbuf)), &cbbr, - &ovs[0], + &ov, 0, ); err != nil && !errors.Is(err, syscall.ERROR_IO_PENDING) { t.Fatalf("could not perform the WSAIoctl: %v", err) @@ -167,14 +158,10 @@ func TestWSASocketConflict(t *testing.T) { if err != nil && errors.Is(err, syscall.ERROR_IO_PENDING) { // It is possible that the overlapped I/O operation completed // immediately so there is no need to wait for it to complete. - if res, err := syscall.WaitForSingleObject(ovs[0].HEvent, syscall.INFINITE); res != 0 { + if res, err := syscall.WaitForSingleObject(ov.HEvent, syscall.INFINITE); res != 0 { t.Fatalf("waiting for the completion of the overlapped IO failed: %v", err) } } - - if err = syscall.CloseHandle(ovs[0].HEvent); err != nil { - t.Fatalf("could not close the event handle: %v", err) - } } type _TCP_INFO_v0 struct { diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go index 59377bc588..8096c64e7e 100644 --- a/src/runtime/netpoll_windows.go +++ b/src/runtime/netpoll_windows.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" "unsafe" ) @@ -13,19 +14,82 @@ const _DWORD_MAX = 0xffffffff const _INVALID_HANDLE_VALUE = ^uintptr(0) -// net_op must be the same as beginning of internal/poll.operation. +// Sources are used to identify the event that created an overlapped entry. +// The source values are arbitrary. There is no risk of collision with user +// defined values because the only way to set the key of an overlapped entry +// is using the iocphandle, which is not accessible to user code. +const ( + netpollSourceReady = iota + 1 + netpollSourceBreak +) + +const ( + // sourceBits is the number of bits needed to represent a source. + // 4 bits can hold 16 different sources, which is more than enough. + // It is set to a low value so the overlapped entry key can + // contain as much bits as possible for the pollDesc pointer. + sourceBits = 4 // 4 bits can hold 16 different sources, which is more than enough. + sourceMasks = 1< (1<