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