diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:19:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:19:13 +0000 |
commit | ccd992355df7192993c666236047820244914598 (patch) | |
tree | f00fea65147227b7743083c6148396f74cd66935 /src/internal/poll/fd_windows_test.go | |
parent | Initial commit. (diff) | |
download | golang-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.go | 198 |
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 +} |