github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/internal/poll/fd_windows_test.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package poll_test 6 7 import ( 8 "errors" 9 "fmt" 10 "internal/poll" 11 "internal/syscall/windows" 12 "os" 13 "sync" 14 "syscall" 15 "testing" 16 "unsafe" 17 ) 18 19 type loggedFD struct { 20 Net string 21 FD *poll.FD 22 Err error 23 } 24 25 var ( 26 logMu sync.Mutex 27 loggedFDs map[syscall.Handle]*loggedFD 28 ) 29 30 func logFD(net string, fd *poll.FD, err error) { 31 logMu.Lock() 32 defer logMu.Unlock() 33 34 loggedFDs[fd.Sysfd] = &loggedFD{ 35 Net: net, 36 FD: fd, 37 Err: err, 38 } 39 } 40 41 func init() { 42 loggedFDs = make(map[syscall.Handle]*loggedFD) 43 *poll.LogInitFD = logFD 44 } 45 46 func findLoggedFD(h syscall.Handle) (lfd *loggedFD, found bool) { 47 logMu.Lock() 48 defer logMu.Unlock() 49 50 lfd, found = loggedFDs[h] 51 return lfd, found 52 } 53 54 // checkFileIsNotPartOfNetpoll verifies that f is not managed by netpoll. 55 // It returns error, if check fails. 56 func checkFileIsNotPartOfNetpoll(f *os.File) error { 57 lfd, found := findLoggedFD(syscall.Handle(f.Fd())) 58 if !found { 59 return fmt.Errorf("%v fd=%v: is not found in the log", f.Name(), f.Fd()) 60 } 61 if lfd.FD.IsPartOfNetpoll() { 62 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) 63 } 64 return nil 65 } 66 67 func TestFileFdsAreInitialised(t *testing.T) { 68 exe, err := os.Executable() 69 if err != nil { 70 t.Fatal(err) 71 } 72 f, err := os.Open(exe) 73 if err != nil { 74 t.Fatal(err) 75 } 76 defer f.Close() 77 78 err = checkFileIsNotPartOfNetpoll(f) 79 if err != nil { 80 t.Fatal(err) 81 } 82 } 83 84 func TestSerialFdsAreInitialised(t *testing.T) { 85 for _, name := range []string{"COM1", "COM2", "COM3", "COM4"} { 86 t.Run(name, func(t *testing.T) { 87 h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name), 88 syscall.GENERIC_READ|syscall.GENERIC_WRITE, 89 0, 90 nil, 91 syscall.OPEN_EXISTING, 92 syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED, 93 0) 94 if err != nil { 95 if errno, ok := err.(syscall.Errno); ok { 96 switch errno { 97 case syscall.ERROR_FILE_NOT_FOUND, 98 syscall.ERROR_ACCESS_DENIED: 99 t.Log("Skipping: ", err) 100 return 101 } 102 } 103 t.Fatal(err) 104 } 105 f := os.NewFile(uintptr(h), name) 106 defer f.Close() 107 108 err = checkFileIsNotPartOfNetpoll(f) 109 if err != nil { 110 t.Fatal(err) 111 } 112 }) 113 } 114 } 115 116 func TestWSASocketConflict(t *testing.T) { 117 s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_OVERLAPPED) 118 if err != nil { 119 t.Fatal(err) 120 } 121 fd := poll.FD{Sysfd: s, IsStream: true, ZeroReadIsEOF: true} 122 _, err = fd.Init("tcp", true) 123 if err != nil { 124 syscall.CloseHandle(s) 125 t.Fatal(err) 126 } 127 defer fd.Close() 128 129 const SIO_TCP_INFO = syscall.IOC_INOUT | syscall.IOC_VENDOR | 39 130 inbuf := uint32(0) 131 var outbuf _TCP_INFO_v0 132 cbbr := uint32(0) 133 134 var ovs []syscall.Overlapped = make([]syscall.Overlapped, 2) 135 // Attempt to exercise behavior where a user-owned syscall.Overlapped 136 // induces an invalid pointer dereference in the Windows-specific version 137 // of runtime.netpoll. 138 ovs[1].Internal -= 1 139 140 // Create an event so that we can efficiently wait for completion 141 // of a requested overlapped I/O operation. 142 ovs[0].HEvent, _ = windows.CreateEvent(nil, 0, 0, nil) 143 if ovs[0].HEvent == 0 { 144 t.Fatalf("could not create the event!") 145 } 146 147 // Set the low bit of the Event Handle so that the completion 148 // of the overlapped I/O event will not trigger a completion event 149 // on any I/O completion port associated with the handle. 150 ovs[0].HEvent |= 0x1 151 152 if err = fd.WSAIoctl( 153 SIO_TCP_INFO, 154 (*byte)(unsafe.Pointer(&inbuf)), 155 uint32(unsafe.Sizeof(inbuf)), 156 (*byte)(unsafe.Pointer(&outbuf)), 157 uint32(unsafe.Sizeof(outbuf)), 158 &cbbr, 159 &ovs[0], 160 0, 161 ); err != nil && !errors.Is(err, syscall.ERROR_IO_PENDING) { 162 t.Fatalf("could not perform the WSAIoctl: %v", err) 163 } 164 165 if err != nil && errors.Is(err, syscall.ERROR_IO_PENDING) { 166 // It is possible that the overlapped I/O operation completed 167 // immediately so there is no need to wait for it to complete. 168 if res, err := syscall.WaitForSingleObject(ovs[0].HEvent, syscall.INFINITE); res != 0 { 169 t.Fatalf("waiting for the completion of the overlapped IO failed: %v", err) 170 } 171 } 172 173 if err = syscall.CloseHandle(ovs[0].HEvent); err != nil { 174 t.Fatalf("could not close the event handle: %v", err) 175 } 176 } 177 178 type _TCP_INFO_v0 struct { 179 State uint32 180 Mss uint32 181 ConnectionTimeMs uint64 182 TimestampsEnabled bool 183 RttUs uint32 184 MinRttUs uint32 185 BytesInFlight uint32 186 Cwnd uint32 187 SndWnd uint32 188 RcvWnd uint32 189 RcvBuf uint32 190 BytesOut uint64 191 BytesIn uint64 192 BytesReordered uint32 193 BytesRetrans uint32 194 FastRetrans uint32 195 DupAcksIn uint32 196 TimeoutEpisodes uint32 197 SynRetrans uint8 198 }