diff options
| author | Katy Moe <katy@katy.moe> | 2021-12-15 12:00:45 +0000 |
|---|---|---|
| committer | Katy Moe <katy@katy.moe> | 2022-04-01 12:32:19 +0100 |
| commit | 4fd729cc0730f1ceef451b7485bf5db3274bf1d6 (patch) | |
| tree | d57dff23cfb047ce58ad30574f68a14b9a51f27d /json | |
| parent | 8c86d68fd91a93c353e1fd5c8622f527523307db (diff) | |
use Go 1.18 native fuzzing
Diffstat (limited to 'json')
27 files changed, 74 insertions, 112 deletions
diff --git a/json/fuzz/README.md b/json/fuzz/README.md index b4d7fd1..7fd0107 100644 --- a/json/fuzz/README.md +++ b/json/fuzz/README.md @@ -1,82 +1,59 @@ # 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). +This directory contains helper functions and corpora that can be used to +fuzz-test the HCL JSON parser using Go's native fuzz testing capabilities. -## Work directory +Please see https://go.dev/doc/fuzz/ for more information on fuzzing. -`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: +## Prerequisites +* Go 1.18 -### macOS - -``` -$ SIZE_IN_MB=1024 -$ DEVICE=`hdiutil attach -nobrowse -nomount ram://$(($SIZE_IN_MB*2048))` -$ diskutil erasevolume HFS+ RamDisk $DEVICE -$ export RAMDISK=/Volumes/RamDisk -``` +## Running the fuzzer -### Linux +Each exported function in the `json` package has a corresponding fuzz test. +These can be run one at a time via `go test`: ``` -$ mkdir /mnt/ramdisk -$ mount -t tmpfs -o size=1024M tmpfs /mnt/ramdisk -$ export RAMDISK=/mnt/ramdisk +$ cd fuzz +$ go test -fuzz FuzzParse ``` -## Running the fuzzer +This command will exit only when a crasher is found (see "Understanding the +result" below). + +## Seed corpus -Next, install `go-fuzz` and its build tool in your `GOPATH`: +The seed corpus for each fuzz test function is stored in the corresponding +directory under `json/fuzz/testdata/fuzz`. For example: ``` -$ make tools FUZZ_WORK_DIR=$RAMDISK +$ ls json/fuzz/testdata/fuzz/FuzzParse +attr-expr.hcl.json +attr-literal.hcl.json +block-attrs.hcl.json +... ``` -Now you can fuzz the parser: +Additional seed inputs can be added to this corpus. Each file must be in the Go 1.18 corpus file format. Files can be converted to this format using the `file2fuzz` tool. To install it: ``` -$ make fuzz-config FUZZ_WORK_DIR=$RAMDISK/json-fuzz-config +$ go install golang.org/x/tools/cmd/file2fuzz@latest +$ file2fuzz -help ``` -~> 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`: +may detect "crashers", which are inputs that caused the parser to panic. +These are written to `json/fuzz/testdata/fuzz/<fuzz test name>/`: ``` -$ ls /tmp/hcl2-fuzz-config/crashers -7f5e9ec80c89da14b8b0b238ec88969f658f5a2d -7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.output -7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.quoted +$ ls json/fuzz/testdata/fuzz/FuzzParseTemplate +582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 ``` -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 -``` +into one of the unit tests in the `json` package and see it crash there +too. After that, it's easy to re-run the test as you try to fix it. diff --git a/json/fuzz/config/corpus/attr-expr.hcl.json b/json/fuzz/config/corpus/attr-expr.hcl.json deleted file mode 100644 index fa9e852..0000000 --- a/json/fuzz/config/corpus/attr-expr.hcl.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "foo": "${upper(bar + baz[1])}" -} diff --git a/json/fuzz/config/corpus/attr-literal.hcl.json b/json/fuzz/config/corpus/attr-literal.hcl.json deleted file mode 100644 index e63d37b..0000000 --- a/json/fuzz/config/corpus/attr-literal.hcl.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "foo": "bar" -} diff --git a/json/fuzz/config/corpus/block-attrs.hcl.json b/json/fuzz/config/corpus/block-attrs.hcl.json deleted file mode 100644 index 4130811..0000000 --- a/json/fuzz/config/corpus/block-attrs.hcl.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "block": { - "foo": true - } -} diff --git a/json/fuzz/config/corpus/block-empty.json b/json/fuzz/config/corpus/block-empty.json deleted file mode 100644 index 6974555..0000000 --- a/json/fuzz/config/corpus/block-empty.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "block": {} -} diff --git a/json/fuzz/config/corpus/block-nested.hcl.json b/json/fuzz/config/corpus/block-nested.hcl.json deleted file mode 100644 index 9d964e0..0000000 --- a/json/fuzz/config/corpus/block-nested.hcl.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "block": { - "another_block": { - "foo": "bar" - } - } -} diff --git a/json/fuzz/config/corpus/empty.hcl.json b/json/fuzz/config/corpus/empty.hcl.json deleted file mode 100644 index 0967ef4..0000000 --- a/json/fuzz/config/corpus/empty.hcl.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/json/fuzz/config/corpus/list-empty.json b/json/fuzz/config/corpus/list-empty.json deleted file mode 100644 index a8471f7..0000000 --- a/json/fuzz/config/corpus/list-empty.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hello": [] -} diff --git a/json/fuzz/config/corpus/list-nested.json b/json/fuzz/config/corpus/list-nested.json deleted file mode 100644 index 27bdf4f..0000000 --- a/json/fuzz/config/corpus/list-nested.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hello": [[]] -} diff --git a/json/fuzz/config/corpus/list-values.json b/json/fuzz/config/corpus/list-values.json deleted file mode 100644 index 6def6cf..0000000 --- a/json/fuzz/config/corpus/list-values.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "hello": [ - "hello", - true, - 1.2 - ] -} diff --git a/json/fuzz/config/corpus/number-big.hcl.json b/json/fuzz/config/corpus/number-big.hcl.json deleted file mode 100644 index 8360c69..0000000 --- a/json/fuzz/config/corpus/number-big.hcl.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "foo": 1.234234e30 -} diff --git a/json/fuzz/config/corpus/number-int.hcl.json b/json/fuzz/config/corpus/number-int.hcl.json deleted file mode 100644 index bab9613..0000000 --- a/json/fuzz/config/corpus/number-int.hcl.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "foo": 1024 -} diff --git a/json/fuzz/config/corpus/utf8.hcl.json b/json/fuzz/config/corpus/utf8.hcl.json deleted file mode 100644 index 55afd36..0000000 --- a/json/fuzz/config/corpus/utf8.hcl.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "foo": "föo ${föo(\"föo\")}" -} diff --git a/json/fuzz/config/fuzz.go b/json/fuzz/config/fuzz.go deleted file mode 100644 index aa6214e..0000000 --- a/json/fuzz/config/fuzz.go +++ /dev/null @@ -1,15 +0,0 @@ -package fuzzconfig - -import ( - "github.com/hashicorp/hcl/v2/json" -) - -func Fuzz(data []byte) int { - _, diags := json.Parse(data, "<fuzz-conf>") - - if diags.HasErrors() { - return 0 - } - - return 1 -} diff --git a/json/fuzz/fuzz_test.go b/json/fuzz/fuzz_test.go new file mode 100644 index 0000000..bcaa4f4 --- /dev/null +++ b/json/fuzz/fuzz_test.go @@ -0,0 +1,20 @@ +package fuzzjson + +import ( + "testing" + + "github.com/hashicorp/hcl/v2/json" +) + +func FuzzParse(f *testing.F) { +f.Fuzz(func(t *testing.T, data []byte) { + _, diags := json.Parse(data, "<fuzz-conf>") + +if diags.HasErrors() { + t.Logf("Error when parsing JSON %v", data) + for _, diag := range diags { + t.Logf("- %s", diag.Error()) + } + } +}) +} diff --git a/json/fuzz/testdata/fuzz/FuzzParse/attr-expr.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/attr-expr.hcl.json new file mode 100644 index 0000000..8620bcd --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/attr-expr.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"foo\": \"${upper(bar + baz[1])}\"\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/attr-literal.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/attr-literal.hcl.json new file mode 100644 index 0000000..32e599d --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/attr-literal.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"foo\": \"bar\"\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/block-attrs.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/block-attrs.hcl.json new file mode 100644 index 0000000..45ec09f --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/block-attrs.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"block\": {\n \"foo\": true\n }\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/block-empty.json b/json/fuzz/testdata/fuzz/FuzzParse/block-empty.json new file mode 100644 index 0000000..88d7816 --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/block-empty.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"block\": {}\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/block-nested.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/block-nested.hcl.json new file mode 100644 index 0000000..276096a --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/block-nested.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"block\": {\n \"another_block\": {\n \"foo\": \"bar\"\n }\n }\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/empty.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/empty.hcl.json new file mode 100644 index 0000000..fbe4fab --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/empty.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/list-empty.json b/json/fuzz/testdata/fuzz/FuzzParse/list-empty.json new file mode 100644 index 0000000..ad1b075 --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/list-empty.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"hello\": []\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/list-nested.json b/json/fuzz/testdata/fuzz/FuzzParse/list-nested.json new file mode 100644 index 0000000..fb2e779 --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/list-nested.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"hello\": [[]]\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/list-values.json b/json/fuzz/testdata/fuzz/FuzzParse/list-values.json new file mode 100644 index 0000000..8b2d4e9 --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/list-values.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"hello\": [\n \"hello\",\n true,\n 1.2\n ]\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/number-big.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/number-big.hcl.json new file mode 100644 index 0000000..7b59d7d --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/number-big.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"foo\": 1.234234e30\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/number-int.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/number-int.hcl.json new file mode 100644 index 0000000..0a6f064 --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/number-int.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"foo\": 1024\n}\n")
\ No newline at end of file diff --git a/json/fuzz/testdata/fuzz/FuzzParse/utf8.hcl.json b/json/fuzz/testdata/fuzz/FuzzParse/utf8.hcl.json new file mode 100644 index 0000000..b20b2c6 --- /dev/null +++ b/json/fuzz/testdata/fuzz/FuzzParse/utf8.hcl.json @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\n \"foo\": \"föo ${föo(\\\"föo\\\")}\"\n}\n")
\ No newline at end of file |
