diff --git a/hcl/json/fuzz/.gitignore b/hcl/json/fuzz/.gitignore new file mode 100644 index 0000000..9490851 --- /dev/null +++ b/hcl/json/fuzz/.gitignore @@ -0,0 +1 @@ +fuzz*-fuzz.zip diff --git a/hcl/json/fuzz/Makefile b/hcl/json/fuzz/Makefile new file mode 100644 index 0000000..2745616 --- /dev/null +++ b/hcl/json/fuzz/Makefile @@ -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 github.com/hashicorp/hcl2/hcl/json/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 diff --git a/hcl/json/fuzz/README.md b/hcl/json/fuzz/README.md new file mode 100644 index 0000000..17f8f9f --- /dev/null +++ b/hcl/json/fuzz/README.md @@ -0,0 +1,57 @@ +# JSON syntax fuzzing utilities + +This directory contains helper functions and corpuses that can be used to +fuzz-test the HCL JSON parser 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 +``` + +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 `hcl/json` 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 +``` diff --git a/hcl/json/fuzz/config/corpus/attr-expr.hcl.json b/hcl/json/fuzz/config/corpus/attr-expr.hcl.json new file mode 100644 index 0000000..fa9e852 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/attr-expr.hcl.json @@ -0,0 +1,3 @@ +{ + "foo": "${upper(bar + baz[1])}" +} diff --git a/hcl/json/fuzz/config/corpus/attr-literal.hcl.json b/hcl/json/fuzz/config/corpus/attr-literal.hcl.json new file mode 100644 index 0000000..e63d37b --- /dev/null +++ b/hcl/json/fuzz/config/corpus/attr-literal.hcl.json @@ -0,0 +1,3 @@ +{ + "foo": "bar" +} diff --git a/hcl/json/fuzz/config/corpus/block-attrs.hcl.json b/hcl/json/fuzz/config/corpus/block-attrs.hcl.json new file mode 100644 index 0000000..4130811 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/block-attrs.hcl.json @@ -0,0 +1,5 @@ +{ + "block": { + "foo": true + } +} diff --git a/hcl/json/fuzz/config/corpus/block-empty.json b/hcl/json/fuzz/config/corpus/block-empty.json new file mode 100644 index 0000000..6974555 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/block-empty.json @@ -0,0 +1,3 @@ +{ + "block": {} +} diff --git a/hcl/json/fuzz/config/corpus/block-nested.hcl.json b/hcl/json/fuzz/config/corpus/block-nested.hcl.json new file mode 100644 index 0000000..9d964e0 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/block-nested.hcl.json @@ -0,0 +1,7 @@ +{ + "block": { + "another_block": { + "foo": "bar" + } + } +} diff --git a/hcl/json/fuzz/config/corpus/empty.hcl.json b/hcl/json/fuzz/config/corpus/empty.hcl.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/empty.hcl.json @@ -0,0 +1 @@ +{} diff --git a/hcl/json/fuzz/config/corpus/list-empty.json b/hcl/json/fuzz/config/corpus/list-empty.json new file mode 100644 index 0000000..a8471f7 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/list-empty.json @@ -0,0 +1,3 @@ +{ + "hello": [] +} diff --git a/hcl/json/fuzz/config/corpus/list-nested.json b/hcl/json/fuzz/config/corpus/list-nested.json new file mode 100644 index 0000000..27bdf4f --- /dev/null +++ b/hcl/json/fuzz/config/corpus/list-nested.json @@ -0,0 +1,3 @@ +{ + "hello": [[]] +} diff --git a/hcl/json/fuzz/config/corpus/list-values.json b/hcl/json/fuzz/config/corpus/list-values.json new file mode 100644 index 0000000..6def6cf --- /dev/null +++ b/hcl/json/fuzz/config/corpus/list-values.json @@ -0,0 +1,7 @@ +{ + "hello": [ + "hello", + true, + 1.2 + ] +} diff --git a/hcl/json/fuzz/config/corpus/number-big.hcl.json b/hcl/json/fuzz/config/corpus/number-big.hcl.json new file mode 100644 index 0000000..8360c69 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/number-big.hcl.json @@ -0,0 +1,3 @@ +{ + "foo": 1.234234e30 +} diff --git a/hcl/json/fuzz/config/corpus/number-int.hcl.json b/hcl/json/fuzz/config/corpus/number-int.hcl.json new file mode 100644 index 0000000..bab9613 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/number-int.hcl.json @@ -0,0 +1,3 @@ +{ + "foo": 1024 +} diff --git a/hcl/json/fuzz/config/corpus/utf8.hcl.json b/hcl/json/fuzz/config/corpus/utf8.hcl.json new file mode 100644 index 0000000..55afd36 --- /dev/null +++ b/hcl/json/fuzz/config/corpus/utf8.hcl.json @@ -0,0 +1,3 @@ +{ + "foo": "föo ${föo(\"föo\")}" +} diff --git a/hcl/json/fuzz/config/fuzz.go b/hcl/json/fuzz/config/fuzz.go new file mode 100644 index 0000000..3455974 --- /dev/null +++ b/hcl/json/fuzz/config/fuzz.go @@ -0,0 +1,15 @@ +package fuzzconfig + +import ( + "github.com/hashicorp/hcl2/hcl/json" +) + +func Fuzz(data []byte) int { + _, diags := json.Parse(data, "") + + if diags.HasErrors() { + return 0 + } + + return 1 +}