Source file src/internal/poll/fd_windows_test.go

     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  }
   199  

View as plain text