summaryrefslogtreecommitdiffstats
path: root/src/internal/poll/fd_windows_test.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/internal/poll/fd_windows_test.go
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/internal/poll/fd_windows_test.go')
-rw-r--r--src/internal/poll/fd_windows_test.go198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/internal/poll/fd_windows_test.go b/src/internal/poll/fd_windows_test.go
new file mode 100644
index 0000000..f0697a0
--- /dev/null
+++ b/src/internal/poll/fd_windows_test.go
@@ -0,0 +1,198 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package poll_test
+
+import (
+ "errors"
+ "fmt"
+ "internal/poll"
+ "internal/syscall/windows"
+ "os"
+ "sync"
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+type loggedFD struct {
+ Net string
+ FD *poll.FD
+ Err error
+}
+
+var (
+ logMu sync.Mutex
+ loggedFDs map[syscall.Handle]*loggedFD
+)
+
+func logFD(net string, fd *poll.FD, err error) {
+ logMu.Lock()
+ defer logMu.Unlock()
+
+ loggedFDs[fd.Sysfd] = &loggedFD{
+ Net: net,
+ FD: fd,
+ Err: err,
+ }
+}
+
+func init() {
+ loggedFDs = make(map[syscall.Handle]*loggedFD)
+ *poll.LogInitFD = logFD
+}
+
+func findLoggedFD(h syscall.Handle) (lfd *loggedFD, found bool) {
+ logMu.Lock()
+ defer logMu.Unlock()
+
+ lfd, found = loggedFDs[h]
+ return lfd, found
+}
+
+// checkFileIsNotPartOfNetpoll verifies that f is not managed by netpoll.
+// It returns error, if check fails.
+func checkFileIsNotPartOfNetpoll(f *os.File) error {
+ lfd, found := findLoggedFD(syscall.Handle(f.Fd()))
+ if !found {
+ return fmt.Errorf("%v fd=%v: is not found in the log", f.Name(), f.Fd())
+ }
+ if lfd.FD.IsPartOfNetpoll() {
+ return fmt.Errorf("%v fd=%v: is part of netpoll, but should not be (logged: net=%v err=%v)", f.Name(), f.Fd(), lfd.Net, lfd.Err)
+ }
+ return nil
+}
+
+func TestFileFdsAreInitialised(t *testing.T) {
+ exe, err := os.Executable()
+ if err != nil {
+ t.Fatal(err)
+ }
+ f, err := os.Open(exe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ err = checkFileIsNotPartOfNetpoll(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestSerialFdsAreInitialised(t *testing.T) {
+ for _, name := range []string{"COM1", "COM2", "COM3", "COM4"} {
+ t.Run(name, func(t *testing.T) {
+ h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
+ syscall.GENERIC_READ|syscall.GENERIC_WRITE,
+ 0,
+ nil,
+ syscall.OPEN_EXISTING,
+ syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED,
+ 0)
+ if err != nil {
+ if errno, ok := err.(syscall.Errno); ok {
+ switch errno {
+ case syscall.ERROR_FILE_NOT_FOUND,
+ syscall.ERROR_ACCESS_DENIED:
+ t.Log("Skipping: ", err)
+ return
+ }
+ }
+ t.Fatal(err)
+ }
+ f := os.NewFile(uintptr(h), name)
+ defer f.Close()
+
+ err = checkFileIsNotPartOfNetpoll(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ })
+ }
+}
+
+func TestWSASocketConflict(t *testing.T) {
+ s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_OVERLAPPED)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fd := poll.FD{Sysfd: s, IsStream: true, ZeroReadIsEOF: true}
+ _, err = fd.Init("tcp", true)
+ if err != nil {
+ syscall.CloseHandle(s)
+ t.Fatal(err)
+ }
+ defer fd.Close()
+
+ const SIO_TCP_INFO = syscall.IOC_INOUT | syscall.IOC_VENDOR | 39
+ inbuf := uint32(0)
+ 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
+
+ // 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 {
+ 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
+
+ if err = fd.WSAIoctl(
+ SIO_TCP_INFO,
+ (*byte)(unsafe.Pointer(&inbuf)),
+ uint32(unsafe.Sizeof(inbuf)),
+ (*byte)(unsafe.Pointer(&outbuf)),
+ uint32(unsafe.Sizeof(outbuf)),
+ &cbbr,
+ &ovs[0],
+ 0,
+ ); err != nil && !errors.Is(err, syscall.ERROR_IO_PENDING) {
+ t.Fatalf("could not perform the WSAIoctl: %v", err)
+ }
+
+ 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 {
+ 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 {
+ State uint32
+ Mss uint32
+ ConnectionTimeMs uint64
+ TimestampsEnabled bool
+ RttUs uint32
+ MinRttUs uint32
+ BytesInFlight uint32
+ Cwnd uint32
+ SndWnd uint32
+ RcvWnd uint32
+ RcvBuf uint32
+ BytesOut uint64
+ BytesIn uint64
+ BytesReordered uint32
+ BytesRetrans uint32
+ FastRetrans uint32
+ DupAcksIn uint32
+ TimeoutEpisodes uint32
+ SynRetrans uint8
+}