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
|
// 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)
}
}
|