whatcanGOwrong
This commit is contained in:
+130
@@ -0,0 +1,130 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
// file2fuzz converts binary files, such as those used by go-fuzz, to the Go
|
||||
// fuzzing corpus format.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// file2fuzz [-o output] [input...]
|
||||
//
|
||||
// The default behavior is to read input from stdin and write the converted
|
||||
// output to stdout. If any position arguments are provided stdin is ignored
|
||||
// and the arguments are assumed to be input files to convert.
|
||||
//
|
||||
// The -o flag provides an path to write output files to. If only one positional
|
||||
// argument is specified it may be a file path or an existing directory, if there are
|
||||
// multiple inputs specified it must be a directory. If a directory is provided
|
||||
// the name of the file will be the SHA-256 hash of its contents.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// encVersion1 is version 1 Go fuzzer corpus encoding.
|
||||
var encVersion1 = "go test fuzz v1"
|
||||
|
||||
func encodeByteSlice(b []byte) []byte {
|
||||
return []byte(fmt.Sprintf("%s\n[]byte(%q)", encVersion1, b))
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: file2fuzz [-o output] [input...]\nconverts files to Go fuzzer corpus format\n")
|
||||
fmt.Fprintf(os.Stderr, "\tinput: files to convert\n")
|
||||
fmt.Fprintf(os.Stderr, "\t-o: where to write converted file(s)\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
func dirWriter(dir string) func([]byte) error {
|
||||
return func(b []byte) error {
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256(b))
|
||||
name := filepath.Join(dir, sum)
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(name, b, 0666); err != nil {
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func convert(inputArgs []string, outputArg string) error {
|
||||
var input []io.Reader
|
||||
if args := inputArgs; len(args) == 0 {
|
||||
input = []io.Reader{os.Stdin}
|
||||
} else {
|
||||
for _, a := range args {
|
||||
f, err := os.Open(a)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open %q: %s", a, err)
|
||||
}
|
||||
defer f.Close()
|
||||
if fi, err := f.Stat(); err != nil {
|
||||
return fmt.Errorf("unable to open %q: %s", a, err)
|
||||
} else if fi.IsDir() {
|
||||
return fmt.Errorf("%q is a directory, not a file", a)
|
||||
}
|
||||
input = append(input, f)
|
||||
}
|
||||
}
|
||||
|
||||
var output func([]byte) error
|
||||
if outputArg == "" {
|
||||
if len(inputArgs) > 1 {
|
||||
return errors.New("-o required with multiple input files")
|
||||
}
|
||||
output = func(b []byte) error {
|
||||
_, err := os.Stdout.Write(b)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if len(inputArgs) > 1 {
|
||||
output = dirWriter(outputArg)
|
||||
} else {
|
||||
if fi, err := os.Stat(outputArg); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("unable to open %q for writing: %s", outputArg, err)
|
||||
} else if err == nil && fi.IsDir() {
|
||||
output = dirWriter(outputArg)
|
||||
} else {
|
||||
output = func(b []byte) error {
|
||||
return os.WriteFile(outputArg, b, 0666)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range input {
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read input: %s", err)
|
||||
}
|
||||
if err := output(encodeByteSlice(b)); err != nil {
|
||||
return fmt.Errorf("unable to write output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("file2fuzz: ")
|
||||
|
||||
output := flag.String("o", "", "where to write converted file(s)")
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if err := convert(flag.Args(), *output); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// Copyright 2021 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 main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if os.Getenv("GO_FILE2FUZZ_TEST_IS_FILE2FUZZ") != "" {
|
||||
main()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
var f2f struct {
|
||||
once sync.Once
|
||||
path string
|
||||
err error
|
||||
}
|
||||
|
||||
func file2fuzz(t *testing.T, dir string, args []string, stdin string) (string, bool) {
|
||||
testenv.NeedsExec(t)
|
||||
|
||||
f2f.once.Do(func() {
|
||||
f2f.path, f2f.err = os.Executable()
|
||||
})
|
||||
if f2f.err != nil {
|
||||
t.Fatal(f2f.err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(f2f.path, args...)
|
||||
cmd.Dir = dir
|
||||
cmd.Env = append(os.Environ(), "PWD="+dir, "GO_FILE2FUZZ_TEST_IS_FILE2FUZZ=1")
|
||||
if stdin != "" {
|
||||
cmd.Stdin = strings.NewReader(stdin)
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return string(out), true
|
||||
}
|
||||
return string(out), false
|
||||
}
|
||||
|
||||
func TestFile2Fuzz(t *testing.T) {
|
||||
type file struct {
|
||||
name string
|
||||
dir bool
|
||||
content string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
stdin string
|
||||
inputFiles []file
|
||||
expectedStdout string
|
||||
expectedFiles []file
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "stdin, stdout",
|
||||
stdin: "hello",
|
||||
expectedStdout: "go test fuzz v1\n[]byte(\"hello\")",
|
||||
},
|
||||
{
|
||||
name: "stdin, output file",
|
||||
stdin: "hello",
|
||||
args: []string{"-o", "output"},
|
||||
expectedFiles: []file{{name: "output", content: "go test fuzz v1\n[]byte(\"hello\")"}},
|
||||
},
|
||||
{
|
||||
name: "stdin, output directory",
|
||||
stdin: "hello",
|
||||
args: []string{"-o", "output"},
|
||||
inputFiles: []file{{name: "output", dir: true}},
|
||||
expectedFiles: []file{{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}},
|
||||
},
|
||||
{
|
||||
name: "input file, output file",
|
||||
args: []string{"-o", "output", "input"},
|
||||
inputFiles: []file{{name: "input", content: "hello"}},
|
||||
expectedFiles: []file{{name: "output", content: "go test fuzz v1\n[]byte(\"hello\")"}},
|
||||
},
|
||||
{
|
||||
name: "input file, output directory",
|
||||
args: []string{"-o", "output", "input"},
|
||||
inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}},
|
||||
expectedFiles: []file{{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}},
|
||||
},
|
||||
{
|
||||
name: "input files, output directory",
|
||||
args: []string{"-o", "output", "input", "input-2"},
|
||||
inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}, {name: "input-2", content: "hello :)"}},
|
||||
expectedFiles: []file{
|
||||
{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"},
|
||||
{name: "output/28059db30ce420ff65b2c29b749804c69c601aeca21b3cbf0644244ff080d7a5", content: "go test fuzz v1\n[]byte(\"hello :)\")"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "input files, no output",
|
||||
args: []string{"input", "input-2"},
|
||||
inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}, {name: "input-2", content: "hello :)"}},
|
||||
expectedError: "file2fuzz: -o required with multiple input files\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tmp, err := os.MkdirTemp(os.TempDir(), "file2fuzz")
|
||||
if err != nil {
|
||||
t.Fatalf("os.MkdirTemp failed: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
for _, f := range tc.inputFiles {
|
||||
if f.dir {
|
||||
if err := os.Mkdir(filepath.Join(tmp, f.name), 0777); err != nil {
|
||||
t.Fatalf("failed to create test directory: %s", err)
|
||||
}
|
||||
} else {
|
||||
if err := os.WriteFile(filepath.Join(tmp, f.name), []byte(f.content), 0666); err != nil {
|
||||
t.Fatalf("failed to create test input file: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out, failed := file2fuzz(t, tmp, tc.args, tc.stdin)
|
||||
if failed && tc.expectedError == "" {
|
||||
t.Fatalf("file2fuzz failed unexpectedly: %s", out)
|
||||
} else if failed && out != tc.expectedError {
|
||||
t.Fatalf("file2fuzz returned unexpected error: got %q, want %q", out, tc.expectedError)
|
||||
}
|
||||
if !failed && out != tc.expectedStdout {
|
||||
t.Fatalf("file2fuzz unexpected stdout: got %q, want %q", out, tc.expectedStdout)
|
||||
}
|
||||
|
||||
for _, f := range tc.expectedFiles {
|
||||
c, err := os.ReadFile(filepath.Join(tmp, f.name))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read expected output file %q: %s", f.name, err)
|
||||
}
|
||||
if string(c) != f.content {
|
||||
t.Fatalf("expected output file %q contains unexpected content: got %s, want %s", f.name, string(c), f.content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user