2020-10-02 16:05:33 -04:00
|
|
|
// 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"
|
2020-10-16 17:42:39 -04:00
|
|
|
"reflect"
|
2020-10-02 16:05:33 -04:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"syscall"
|
2020-10-16 17:42:39 -04:00
|
|
|
"unsafe"
|
2020-10-02 16:05:33 -04:00
|
|
|
)
|
|
|
|
|
|
2020-10-16 17:42:39 -04:00
|
|
|
type sharedMemSys struct {
|
|
|
|
|
mapObj syscall.Handle
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) {
|
|
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-02 16:05:33 -04:00
|
|
|
// setWorkerComm configures communciation channels on the cmd that will
|
|
|
|
|
// run a worker process.
|
2020-10-16 17:42:39 -04:00
|
|
|
func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
|
|
|
|
|
syscall.SetHandleInformation(syscall.Handle(comm.fuzzIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
|
|
|
|
|
syscall.SetHandleInformation(syscall.Handle(comm.fuzzOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
|
|
|
|
|
syscall.SetHandleInformation(syscall.Handle(comm.mem.f.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
|
|
|
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GO_TEST_FUZZ_WORKER_HANDLES=%x,%x,%x", comm.fuzzIn.Fd(), comm.fuzzOut.Fd(), comm.mem.f.Fd()))
|
2020-10-02 16:05:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getWorkerComm returns communication channels in the worker process.
|
2020-10-16 17:42:39 -04:00
|
|
|
func getWorkerComm() (comm workerComm, err error) {
|
2020-10-02 16:05:33 -04:00
|
|
|
v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES")
|
|
|
|
|
if v == "" {
|
2020-10-16 17:42:39 -04:00
|
|
|
return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES not set")
|
2020-10-02 16:05:33 -04:00
|
|
|
}
|
|
|
|
|
parts := strings.Split(v, ",")
|
2020-10-16 17:42:39 -04:00
|
|
|
if len(parts) != 3 {
|
|
|
|
|
return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES has invalid value")
|
2020-10-02 16:05:33 -04:00
|
|
|
}
|
|
|
|
|
base := 16
|
|
|
|
|
bitSize := 64
|
2020-10-16 17:42:39 -04:00
|
|
|
handles := make([]syscall.Handle, len(parts))
|
|
|
|
|
for i, s := range parts {
|
|
|
|
|
h, err := strconv.ParseInt(s, base, bitSize)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES has invalid value: %v", err)
|
|
|
|
|
}
|
|
|
|
|
handles[i] = syscall.Handle(h)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fuzzIn := os.NewFile(uintptr(handles[0]), "fuzz_in")
|
|
|
|
|
fuzzOut := os.NewFile(uintptr(handles[1]), "fuzz_out")
|
|
|
|
|
tmpFile := os.NewFile(uintptr(handles[2]), "fuzz_mem")
|
|
|
|
|
fi, err := tmpFile.Stat()
|
2020-10-02 16:05:33 -04:00
|
|
|
if err != nil {
|
2020-10-16 17:42:39 -04:00
|
|
|
return workerComm{}, err
|
2020-10-02 16:05:33 -04:00
|
|
|
}
|
2020-10-16 17:42:39 -04:00
|
|
|
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)
|
2020-10-02 16:05:33 -04:00
|
|
|
if err != nil {
|
2020-10-16 17:42:39 -04:00
|
|
|
return workerComm{}, err
|
2020-10-02 16:05:33 -04:00
|
|
|
}
|
2020-10-16 17:42:39 -04:00
|
|
|
|
|
|
|
|
return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, mem: mem}, nil
|
2020-10-02 16:05:33 -04:00
|
|
|
}
|