Source file src/internal/fuzz/sys_windows.go

     1  // Copyright 2020 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 fuzz
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"os/exec"
    11  	"reflect"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  type sharedMemSys struct {
    17  	mapObj syscall.Handle
    18  }
    19  
    20  func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (mem *sharedMem, err error) {
    21  	defer func() {
    22  		if err != nil {
    23  			err = fmt.Errorf("mapping temporary file %s: %w", f.Name(), err)
    24  		}
    25  	}()
    26  
    27  	// Create a file mapping object. The object itself is not shared.
    28  	mapObj, err := syscall.CreateFileMapping(
    29  		syscall.Handle(f.Fd()), // fhandle
    30  		nil,                    // sa
    31  		syscall.PAGE_READWRITE, // prot
    32  		0,                      // maxSizeHigh
    33  		0,                      // maxSizeLow
    34  		nil,                    // name
    35  	)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	// Create a view from the file mapping object.
    41  	access := uint32(syscall.FILE_MAP_READ | syscall.FILE_MAP_WRITE)
    42  	addr, err := syscall.MapViewOfFile(
    43  		mapObj,        // handle
    44  		access,        // access
    45  		0,             // offsetHigh
    46  		0,             // offsetLow
    47  		uintptr(size), // length
    48  	)
    49  	if err != nil {
    50  		syscall.CloseHandle(mapObj)
    51  		return nil, err
    52  	}
    53  
    54  	var region []byte
    55  	header := (*reflect.SliceHeader)(unsafe.Pointer(&region))
    56  	header.Data = addr
    57  	header.Len = size
    58  	header.Cap = size
    59  	return &sharedMem{
    60  		f:             f,
    61  		region:        region,
    62  		removeOnClose: removeOnClose,
    63  		sys:           sharedMemSys{mapObj: mapObj},
    64  	}, nil
    65  }
    66  
    67  // Close unmaps the shared memory and closes the temporary file. If this
    68  // sharedMem was created with sharedMemTempFile, Close also removes the file.
    69  func (m *sharedMem) Close() error {
    70  	// Attempt all operations, even if we get an error for an earlier operation.
    71  	// os.File.Close may fail due to I/O errors, but we still want to delete
    72  	// the temporary file.
    73  	var errs []error
    74  	errs = append(errs,
    75  		syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&m.region[0]))),
    76  		syscall.CloseHandle(m.sys.mapObj),
    77  		m.f.Close())
    78  	if m.removeOnClose {
    79  		errs = append(errs, os.Remove(m.f.Name()))
    80  	}
    81  	for _, err := range errs {
    82  		if err != nil {
    83  			return err
    84  		}
    85  	}
    86  	return nil
    87  }
    88  
    89  // setWorkerComm configures communication channels on the cmd that will
    90  // run a worker process.
    91  func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
    92  	mem := <-comm.memMu
    93  	memName := mem.f.Name()
    94  	comm.memMu <- mem
    95  	syscall.SetHandleInformation(syscall.Handle(comm.fuzzIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
    96  	syscall.SetHandleInformation(syscall.Handle(comm.fuzzOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
    97  	cmd.Env = append(cmd.Env, fmt.Sprintf("GO_TEST_FUZZ_WORKER_HANDLES=%x,%x,%q", comm.fuzzIn.Fd(), comm.fuzzOut.Fd(), memName))
    98  	cmd.SysProcAttr = &syscall.SysProcAttr{AdditionalInheritedHandles: []syscall.Handle{syscall.Handle(comm.fuzzIn.Fd()), syscall.Handle(comm.fuzzOut.Fd())}}
    99  }
   100  
   101  // getWorkerComm returns communication channels in the worker process.
   102  func getWorkerComm() (comm workerComm, err error) {
   103  	v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES")
   104  	if v == "" {
   105  		return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES not set")
   106  	}
   107  	var fuzzInFD, fuzzOutFD uintptr
   108  	var memName string
   109  	if _, err := fmt.Sscanf(v, "%x,%x,%q", &fuzzInFD, &fuzzOutFD, &memName); err != nil {
   110  		return workerComm{}, fmt.Errorf("parsing GO_TEST_FUZZ_WORKER_HANDLES=%s: %v", v, err)
   111  	}
   112  
   113  	fuzzIn := os.NewFile(fuzzInFD, "fuzz_in")
   114  	fuzzOut := os.NewFile(fuzzOutFD, "fuzz_out")
   115  	tmpFile, err := os.OpenFile(memName, os.O_RDWR, 0)
   116  	if err != nil {
   117  		return workerComm{}, fmt.Errorf("worker opening temp file: %w", err)
   118  	}
   119  	fi, err := tmpFile.Stat()
   120  	if err != nil {
   121  		return workerComm{}, fmt.Errorf("worker checking temp file size: %w", err)
   122  	}
   123  	size := int(fi.Size())
   124  	if int64(size) != fi.Size() {
   125  		return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size")
   126  	}
   127  	removeOnClose := false
   128  	mem, err := sharedMemMapFile(tmpFile, size, removeOnClose)
   129  	if err != nil {
   130  		return workerComm{}, err
   131  	}
   132  	memMu := make(chan *sharedMem, 1)
   133  	memMu <- mem
   134  
   135  	return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil
   136  }
   137  
   138  func isInterruptError(err error) bool {
   139  	// On Windows, we can't tell whether the process was interrupted by the error
   140  	// returned by Wait. It looks like an ExitError with status 1.
   141  	return false
   142  }
   143  
   144  // terminationSignal returns -1 and false because Windows doesn't have signals.
   145  func terminationSignal(err error) (os.Signal, bool) {
   146  	return syscall.Signal(-1), false
   147  }
   148  
   149  // isCrashSignal is not implemented because Windows doesn't have signals.
   150  func isCrashSignal(signal os.Signal) bool {
   151  	panic("not implemented: no signals on windows")
   152  }
   153  

View as plain text