hclsyntax: helpers for fuzz testing with go-fuzz

This commit is contained in:
Martin Atkins 2018-02-04 12:21:29 -08:00
parent 18a92d222b
commit 93a7008e3d
36 changed files with 187 additions and 0 deletions

1
hcl/hclsyntax/fuzz/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
fuzz*-fuzz.zip

View File

@ -0,0 +1,28 @@
ifndef FUZZ_WORK_DIR
$(error FUZZ_WORK_DIR is not set)
endif
default:
@echo "See README.md for usage instructions"
fuzz-config: fuzz-exec-config
fuzz-expr: fuzz-exec-expr
fuzz-template: fuzz-exec-template
fuzz-traversal: fuzz-exec-traversal
fuzz-exec-%: fuzz%-fuzz.zip
go-fuzz -bin=./fuzz$*-fuzz.zip -workdir=$(FUZZ_WORK_DIR)
fuzz%-fuzz.zip: %/fuzz.go
go-fuzz-build github.com/hashicorp/hcl2/hcl/hclsyntax/fuzz/$*
tools:
go get -u github.com/dvyukov/go-fuzz/go-fuzz
go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
clean:
rm fuzz*-fuzz.zip
.PHONY: tools clean fuzz-config fuzz-expr fuzz-template fuzz-traversal
.PRECIOUS: fuzzconfig-fuzz.zip fuzzexpr-fuzz.zip fuzztemplate-fuzz.zip fuzztraversal-fuzz.zip

View File

@ -0,0 +1,60 @@
# hclsyntax fuzzing utilities
This directory contains helper functions and corpuses that can be used to
fuzz-test the `hclsyntax` parsers using [go-fuzz](https://github.com/dvyukov/go-fuzz).
To fuzz, first install go-fuzz and its build tool in your `GOPATH`:
```
$ make tools
```
Now you can fuzz one or all of the parsers:
```
$ make fuzz-config FUZZ_WORK_DIR=/tmp/hcl2-fuzz-config
$ make fuzz-expr FUZZ_WORK_DIR=/tmp/hcl2-fuzz-expr
$ make fuzz-template FUZZ_WORK_DIR=/tmp/hcl2-fuzz-template
$ make fuzz-traversal FUZZ_WORK_DIR=/tmp/hcl2-fuzz-traversal
```
In all cases, set `FUZZ_WORK_DIR` to a directory where `go-fuzz` can keep state
as it works. This should ideally be in a ramdisk for efficiency, and should
probably _not_ be on an SSD to avoid thrashing it.
## Understanding the result
A small number of subdirectories will be created in the work directory.
If you let `go-fuzz` run for a few minutes (the more minutes the better) it
may detect "crashers", which are inputs that caused the parser to panic. Details
about these are written to `$FUZZ_WORK_DIR/crashers`:
```
$ ls /tmp/hcl2-fuzz-config/crashers
7f5e9ec80c89da14b8b0b238ec88969f658f5a2d
7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.output
7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.quoted
```
The base file above (with no extension) is the input that caused a crash. The
`.output` file contains the panic stack trace, which you can use as a clue to
figure out what caused the crash.
A good first step to fixing a detected crasher is to copy the failing input
into one of the unit tests in the `hclsyntax` package and see it crash there
too. After that, it's easy to re-run the test as you try to fix it. The
file with the `.quoted` extension contains a form of the input that is quoted
in Go syntax for easy copy-paste into a test case, even if the input contains
non-printable characters or other inconvenient symbols.
## Rebuilding for new Upstream Code
An archive file is created for `go-fuzz` to use on the first run of each
of the above, as a `.zip` file created in this directory. If upstream code
is changed these will need to be deleted to cause them to be rebuilt with
the latest code:
```
$ make clean
```

View File

@ -0,0 +1 @@
foo = upper(bar + baz[1])

View File

@ -0,0 +1 @@
foo = "bar"

View File

@ -0,0 +1,3 @@
block {
foo = true
}

View File

@ -0,0 +1,2 @@
block {
}

View File

@ -0,0 +1,5 @@
block {
another_block {
foo = bar
}
}

View File

@ -0,0 +1 @@
foo = "föo ${föo("föo")}"

View File

@ -0,0 +1,16 @@
package fuzzconfig
import (
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
)
func Fuzz(data []byte) int {
_, diags := hclsyntax.ParseConfig(data, "<fuzz-conf>", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
return 0
}
return 1
}

View File

@ -0,0 +1 @@
""

View File

@ -0,0 +1 @@
"hi $${var.foo}"

View File

@ -0,0 +1 @@
"bar\nbaz"

View File

@ -0,0 +1 @@
title(var.name)

View File

@ -0,0 +1 @@
42

View File

@ -0,0 +1 @@
foo

View File

@ -0,0 +1 @@
foo.bar.*.baz

View File

@ -0,0 +1 @@
foo.bar[*].baz

View File

@ -0,0 +1 @@
föo("föo") + föo

View File

@ -0,0 +1 @@
var.bar

View File

@ -0,0 +1,16 @@
package fuzzexpr
import (
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
)
func Fuzz(data []byte) int {
_, diags := hclsyntax.ParseExpression(data, "<fuzz-expr>", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
return 0
}
return 1
}

View File

@ -0,0 +1 @@
hi $${var.foo}

View File

@ -0,0 +1 @@
foo ${"bar\nbaz"}

View File

@ -0,0 +1 @@
hi ${title(var.name)}

View File

@ -0,0 +1 @@
foo ${42}

View File

@ -0,0 +1 @@
${var.bar}

View File

@ -0,0 +1 @@
foo

View File

@ -0,0 +1 @@
föo ${föo("föo")}

View File

@ -0,0 +1,16 @@
package fuzztemplate
import (
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
)
func Fuzz(data []byte) int {
_, diags := hclsyntax.ParseTemplate(data, "<fuzz-tmpl>", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
return 0
}
return 1
}

View File

@ -0,0 +1 @@
foo.bar

View File

@ -0,0 +1 @@
foo.bar[1].baz["foo"].pizza

View File

@ -0,0 +1 @@
foo[1]

View File

@ -0,0 +1 @@
foo

View File

@ -0,0 +1,16 @@
package fuzztraversal
import (
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
)
func Fuzz(data []byte) int {
_, diags := hclsyntax.ParseTraversalAbs(data, "<fuzz-trav>", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
return 0
}
return 1
}