...

Source file src/github.com/AdamKorcz/go-118-fuzz-build/coverage/coverage.go

Documentation: github.com/AdamKorcz/go-118-fuzz-build/coverage

     1  package coverage
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/parser"
     7  	"go/token"
     8  	"strconv"
     9  	"strings"
    10  
    11  	fuzz "github.com/AdaLogics/go-fuzz-headers"
    12  )
    13  
    14  type Walker struct {
    15  	args []string
    16  	fuzzerName  string
    17  	fset  *token.FileSet
    18  	src  []byte // file contents
    19  }
    20  
    21  // Main walker func to traverse a fuzz harness when obtaining
    22  // the fuzzers args. Does not add the first add (t *testing.T)
    23  func (walker *Walker) Visit(node ast.Node) ast.Visitor {
    24  	if node == nil {
    25  		return walker
    26  	}
    27  	switch n := node.(type) {
    28  	case *ast.FuncDecl:
    29  		if n.Name.Name == walker.fuzzerName {
    30  			bw := &BodyWalker{
    31  				args: make([]string, 0),
    32  				fuzzerName: walker.fuzzerName,
    33  				fset: walker.fset,
    34  				src: walker.src,
    35  			}
    36  			ast.Walk(bw, n.Body)
    37  			walker.args = bw.args
    38  		}
    39  	}
    40  	return walker
    41  }
    42  
    43  type BodyWalker struct {
    44  	args []string
    45  	fuzzerName  string
    46  	fset  *token.FileSet
    47  	src  []byte // file contents
    48  }
    49  
    50  func (walker *BodyWalker) Visit(node ast.Node) ast.Visitor {
    51  	if node == nil {
    52  		return walker
    53  	}
    54  	switch n := node.(type) {
    55  	case *ast.CallExpr:
    56  		if aa, ok := n.Fun.(*ast.SelectorExpr); ok {
    57  			if _, ok := aa.X.(*ast.Ident); ok {
    58  				if aa.X.(*ast.Ident).Name == "f" && aa.Sel.Name == "Fuzz" {
    59  
    60  					// Get the func() arg to f.Fuzz:
    61  					funcArg := n.Args[0].(*ast.FuncLit)
    62  
    63  					walker.addArgs(funcArg.Type.Params.List[1:])
    64  				}
    65  			}
    66  		}
    67  	}
    68  	return walker
    69  }
    70  
    71  // Receives a list of *ast.Field and adds them to the walker
    72  func (walker *BodyWalker) addArgs(n []*ast.Field) {
    73  	for _, names := range n {
    74  		for _, _ = range names.Names {
    75  			if a, ok := names.Type.(*ast.ArrayType); ok {
    76  				walker.addArg(getArrayType(a))
    77  			} else {
    78  				walker.addArg(names.Type.(*ast.Ident).Name)
    79  			}
    80  		}
    81  	}
    82  }
    83  
    84  func (walker *BodyWalker) addArg(arg string) {
    85  	walker.args = append(walker.args, arg)
    86  }
    87  
    88  func getArrayType(n *ast.ArrayType) string {
    89  	typeName := n.Elt.(*ast.Ident).Name
    90  	return fmt.Sprintf("[]%s", typeName)
    91  }
    92  
    93  func getFuzzArgs(fuzzerFileContents, fuzzerName string) ([]string, error) {
    94  	fset := token.NewFileSet()
    95  	f, err := parser.ParseFile(fset, "fuzz_test.go", fuzzerFileContents, 0)
    96  	if err != nil {
    97  		panic(err)
    98  	}
    99  	w := &Walker{
   100  		args: []string{},
   101  		fuzzerName: fuzzerName,
   102  		fset: fset,
   103  		src: []byte(fuzzerFileContents),
   104  	}
   105  	ast.Walk(w, f)
   106  	return w.args, nil
   107  }
   108  
   109  // This is the API that should be called externally.
   110  // Params:
   111  // fuzzerFileContents: the contents of the fuzzerfile. This should be
   112  // obtained with os.ReadFile().
   113  // testCase: The libFuzzer testcase. This should also be obtained
   114  // with os.ReadFile().
   115  func ConvertLibfuzzerSeedToGoSeed(fuzzerFileContents, testCase []byte, fuzzerName string) string {
   116  	args, err := getFuzzArgs(string(fuzzerFileContents), fuzzerName)
   117  	if err != nil {
   118  		panic(err)
   119  	}
   120  	newSeed := libFuzzerSeedToGoSeed(testCase, args)
   121  	return newSeed
   122  }
   123  
   124  // Takes a libFuzzer testcase and returns a Native Go testcase
   125  func libFuzzerSeedToGoSeed(testcase []byte, args []string) string {
   126  	var b strings.Builder
   127  	b.WriteString("go test fuzz v1\n")
   128  
   129  	fuzzConsumer := fuzz.NewConsumer(testcase)
   130  	for argNumber, arg := range args {
   131  		fmt.Println(argNumber)
   132  		switch arg {
   133  		case "[]uint8", "[]byte":
   134  			randBytes, err := fuzzConsumer.GetBytes()
   135  			if err != nil {
   136  				panic(err)
   137  			}
   138  			b.WriteString(fmt.Sprintf("[]byte(\"%s\")", string(randBytes)))
   139  			if argNumber != len(args)-1 {
   140  				b.WriteString("\n")
   141  			}
   142  		case "string":
   143  			s, err := fuzzConsumer.GetString()
   144  			if err != nil {
   145  				panic(err)
   146  			}
   147  			b.WriteString(fmt.Sprintf("string(\"%s\")", s))
   148  			if argNumber != len(args)-1 {
   149  				b.WriteString("\n")
   150  			}
   151  		case "int":
   152  			randInt, err := fuzzConsumer.GetInt()
   153  			if err != nil {
   154  				panic(err)
   155  			}
   156  			b.WriteString(fmt.Sprintf("int(%s)", strconv.Itoa(randInt)))
   157  			if argNumber != len(args)-1 {
   158  				b.WriteString("\n")
   159  			}
   160  		case "int8":
   161  			randInt, err := fuzzConsumer.GetInt()
   162  			if err != nil {
   163  				panic(err)
   164  			}
   165  			b.WriteString(fmt.Sprintf("int8(%s)", strconv.Itoa(randInt)))
   166  			if argNumber != len(args)-1 {
   167  				b.WriteString("\n")
   168  			}
   169  		case "int16":
   170  			randInt, err := fuzzConsumer.GetInt()
   171  			if err != nil {
   172  				panic(err)
   173  			}
   174  			b.WriteString(fmt.Sprintf("int16(%s)", strconv.Itoa(randInt)))
   175  			if argNumber != len(args)-1 {
   176  				b.WriteString("\n")
   177  			}
   178  		case "int32":
   179  			randInt, err := fuzzConsumer.GetInt()
   180  			if err != nil {
   181  				panic(err)
   182  			}
   183  			b.WriteString(fmt.Sprintf("int32(%s)", strconv.Itoa(randInt)))
   184  			if argNumber != len(args)-1 {
   185  				b.WriteString("\n")
   186  			}
   187  		case "int64":
   188  			randInt, err := fuzzConsumer.GetInt()
   189  			if err != nil {
   190  				panic(err)
   191  			}
   192  			b.WriteString(fmt.Sprintf("int64(%s)", strconv.Itoa(randInt)))
   193  			if argNumber != len(args)-1 {
   194  				b.WriteString("\n")
   195  			}
   196  		case "uint":
   197  			randInt, err := fuzzConsumer.GetInt()
   198  			if err != nil {
   199  				panic(err)
   200  			}
   201  			b.WriteString(fmt.Sprintf("uint(%s)", strconv.Itoa(randInt)))
   202  			if argNumber != len(args)-1 {
   203  				b.WriteString("\n")
   204  			}
   205  		case "uint8":
   206  			randInt, err := fuzzConsumer.GetInt()
   207  			if err != nil {
   208  				panic(err)
   209  			}
   210  			b.WriteString(fmt.Sprintf("uint8(%s)", strconv.Itoa(randInt)))
   211  			if argNumber != len(args)-1 {
   212  				b.WriteString("\n")
   213  			}
   214  		case "uint16":
   215  			randInt, err := fuzzConsumer.GetUint16()
   216  			if err != nil {
   217  				panic(err)
   218  			}
   219  			b.WriteString(fmt.Sprintf("uint16(%d)", randInt))
   220  			if argNumber != len(args)-1 {
   221  				b.WriteString("\n")
   222  			}
   223  		case "uint32":
   224  			randInt, err := fuzzConsumer.GetUint32()
   225  			if err != nil {
   226  				panic(err)
   227  			}
   228  			b.WriteString(fmt.Sprintf("uint32(%d)", randInt))
   229  			if argNumber != len(args)-1 {
   230  				b.WriteString("\n")
   231  			}
   232  		case "uint64":
   233  			randInt, err := fuzzConsumer.GetUint64()
   234  			if err != nil {
   235  				panic(err)
   236  			}
   237  			b.WriteString(fmt.Sprintf("uint64(%d)", randInt))
   238  			if argNumber != len(args)-1 {
   239  				b.WriteString("\n")
   240  			}
   241  		case "rune":
   242  			randRune, err := fuzzConsumer.GetRune()
   243  			if err != nil {
   244  				panic(err)
   245  			}
   246  			b.WriteString(fmt.Sprintf("rune(%s)", string(randRune)))
   247  			if argNumber != len(args)-1 {
   248  				b.WriteString("\n")
   249  			}
   250  		case "float32":
   251  			randFloat, err := fuzzConsumer.GetFloat32()
   252  			if err != nil {
   253  				panic(err)
   254  			}
   255  			b.WriteString("float32(")
   256  			b.WriteString(fmt.Sprintf("%f", randFloat))
   257  			b.WriteString(")")
   258  			if argNumber != len(args)-1 {
   259  				b.WriteString("\n")
   260  			}
   261  		case "float64":
   262  			randFloat, err := fuzzConsumer.GetFloat64()
   263  			if err != nil {
   264  				panic(err)
   265  			}
   266  			b.WriteString("float64(")
   267  			b.WriteString(fmt.Sprintf("%f", randFloat))
   268  			b.WriteString(")")
   269  			if argNumber != len(args)-1 {
   270  				b.WriteString("\n")
   271  			}
   272  		case "bool":
   273  			randBool, err := fuzzConsumer.GetBool()
   274  			if err != nil {
   275  				panic(err)
   276  			}
   277  			b.WriteString(fmt.Sprintf("bool(%t)", randBool))
   278  			if argNumber != len(args)-1 {
   279  				b.WriteString("\n")
   280  			}
   281  		default:
   282  			panic("Fuzzer uses unsupported type")
   283  		}
   284  	}
   285  	return b.String()
   286  }
   287  

View as plain text