github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/internal/fuzz/sys_windows.go (about) 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(®ion)) 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 }