Skip to content

Commit

Permalink
hclwrite: Add fuzz testing
Browse files Browse the repository at this point in the history
  • Loading branch information
alisdair committed May 14, 2020
1 parent 148b442 commit b5f1f97
Show file tree
Hide file tree
Showing 33 changed files with 183 additions and 0 deletions.
1 change: 1 addition & 0 deletions hclwrite/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fuzz*-fuzz.zip
25 changes: 25 additions & 0 deletions hclwrite/fuzz/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

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-exec-%: fuzz%-fuzz.zip
go-fuzz -bin=./fuzz$*-fuzz.zip -workdir=$(FUZZ_WORK_DIR)

fuzz%-fuzz.zip: %/fuzz.go
go-fuzz-build -x github.com/hashicorp/hcl/v2/hclwrite/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
.PRECIOUS: fuzzconfig-fuzz.zip
82 changes: 82 additions & 0 deletions hclwrite/fuzz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# hclwrite fuzzing utilities

This directory contains helper functions and corpuses that can be used to
fuzz-test the `hclwrite` package using [go-fuzz](https://github.com/dvyukov/go-fuzz).

## Work directory

`go-fuzz` needs a working directory where it 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. Here's how to create a ramdisk:

### macOS

```
$ SIZE_IN_MB=1024
$ DEVICE=`hdiutil attach -nobrowse -nomount ram:https://$(($SIZE_IN_MB*2048))`
$ diskutil erasevolume HFS+ RamDisk $DEVICE
$ export RAMDISK=/Volumes/RamDisk
```

### Linux

```
$ mkdir /mnt/ramdisk
$ mount -t tmpfs -o size=1024M tmpfs /mnt/ramdisk
$ export RAMDISK=/mnt/ramdisk
```

## Running the fuzzer

Next, install `go-fuzz` and its build tool in your `GOPATH`:

```
$ make tools FUZZ_WORK_DIR=$RAMDISK
```

Now you can fuzz the parser:

```
$ make fuzz-config FUZZ_WORK_DIR=$RAMDISK/hclwrite-fuzz-config
```

~> Note: `go-fuzz` does not interact well with `goenv`. If you encounter build
errors where the package `go.fuzz.main` could not be found, you may need to use
a machine with a direct installation of Go.

## 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 `hclwrite` 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
```
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/attr-expr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo = upper(bar + baz[1])
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/attr-literal.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo = "bar"
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/attr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar
3 changes: 3 additions & 0 deletions hclwrite/fuzz/config/corpus/block-attrs.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
block {
foo = true
}
4 changes: 4 additions & 0 deletions hclwrite/fuzz/config/corpus/block-comment.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* multi
line
comment
*/
2 changes: 2 additions & 0 deletions hclwrite/fuzz/config/corpus/block-empty.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
block {
}
5 changes: 5 additions & 0 deletions hclwrite/fuzz/config/corpus/block-nested.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
block {
another_block {
foo = bar
}
}
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/complex.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar[1].baz["foo"].pizza
Empty file.
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/escape-dollar.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = "hi $${var.foo}"
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/escape-newline.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = "bar\nbaz"
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/function-call-tmpl.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = "b ${title(var.name)}
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/function-call.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = title(var.name)
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/hash-comment.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# another comment
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/index.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo[1]
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/int-tmpl.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = "foo ${42}"
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/int.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = 42
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/just-interp.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = "${var.bar}"
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/literal.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo
14 changes: 14 additions & 0 deletions hclwrite/fuzz/config/corpus/lots-of-comments.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// comment
block {
// another comment
another_block { # comment
// comment
foo = bar
}

/* commented out block
blah {
bar = foo
}
*/
}
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/slash-comment.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// comment
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/splat-attr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar.*.baz
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/splat-dot-full.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar.*
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/splat-full.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar[*].baz
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar.0
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/traversal-dot-index.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar.4.baz
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/traversal-index.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = foo.bar[4].baz
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/utf8.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo = "föo ${föo("föo")}"
1 change: 1 addition & 0 deletions hclwrite/fuzz/config/corpus/var.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a = var.bar
24 changes: 24 additions & 0 deletions hclwrite/fuzz/config/fuzz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package fuzzconfig

import (
"io/ioutil"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
)

func Fuzz(data []byte) int {
file, diags := hclwrite.ParseConfig(data, "<fuzz-conf>", hcl.Pos{Line: 1, Column: 1})

if diags.HasErrors() {
return 0
}

_, err := file.WriteTo(ioutil.Discard)

if err != nil {
return 0
}

return 1
}

0 comments on commit b5f1f97

Please sign in to comment.