hclsyntax: helpers for fuzz testing with go-fuzz
This commit is contained in:
parent
18a92d222b
commit
93a7008e3d
1
hcl/hclsyntax/fuzz/.gitignore
vendored
Normal file
1
hcl/hclsyntax/fuzz/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
fuzz*-fuzz.zip
|
28
hcl/hclsyntax/fuzz/Makefile
Normal file
28
hcl/hclsyntax/fuzz/Makefile
Normal 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
|
60
hcl/hclsyntax/fuzz/README.md
Normal file
60
hcl/hclsyntax/fuzz/README.md
Normal 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
|
||||||
|
```
|
1
hcl/hclsyntax/fuzz/config/corpus/attr-expr.hcl
Normal file
1
hcl/hclsyntax/fuzz/config/corpus/attr-expr.hcl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo = upper(bar + baz[1])
|
1
hcl/hclsyntax/fuzz/config/corpus/attr-literal.hcl
Normal file
1
hcl/hclsyntax/fuzz/config/corpus/attr-literal.hcl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo = "bar"
|
3
hcl/hclsyntax/fuzz/config/corpus/block-attrs.hcl
Normal file
3
hcl/hclsyntax/fuzz/config/corpus/block-attrs.hcl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
block {
|
||||||
|
foo = true
|
||||||
|
}
|
2
hcl/hclsyntax/fuzz/config/corpus/block-empty.hcl
Normal file
2
hcl/hclsyntax/fuzz/config/corpus/block-empty.hcl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
block {
|
||||||
|
}
|
5
hcl/hclsyntax/fuzz/config/corpus/block-nested.hcl
Normal file
5
hcl/hclsyntax/fuzz/config/corpus/block-nested.hcl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
block {
|
||||||
|
another_block {
|
||||||
|
foo = bar
|
||||||
|
}
|
||||||
|
}
|
0
hcl/hclsyntax/fuzz/config/corpus/empty.hcl
Normal file
0
hcl/hclsyntax/fuzz/config/corpus/empty.hcl
Normal file
1
hcl/hclsyntax/fuzz/config/corpus/utf8.hcl
Normal file
1
hcl/hclsyntax/fuzz/config/corpus/utf8.hcl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo = "föo ${föo("föo")}"
|
16
hcl/hclsyntax/fuzz/config/fuzz.go
Normal file
16
hcl/hclsyntax/fuzz/config/fuzz.go
Normal 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
|
||||||
|
}
|
1
hcl/hclsyntax/fuzz/expr/corpus/empty.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/empty.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
""
|
1
hcl/hclsyntax/fuzz/expr/corpus/escape-dollar.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/escape-dollar.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
"hi $${var.foo}"
|
1
hcl/hclsyntax/fuzz/expr/corpus/escape-newline.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/escape-newline.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
"bar\nbaz"
|
1
hcl/hclsyntax/fuzz/expr/corpus/function-call.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/function-call.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
title(var.name)
|
1
hcl/hclsyntax/fuzz/expr/corpus/int.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/int.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
42
|
1
hcl/hclsyntax/fuzz/expr/corpus/literal.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/literal.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo
|
1
hcl/hclsyntax/fuzz/expr/corpus/splat-attr.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/splat-attr.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo.bar.*.baz
|
1
hcl/hclsyntax/fuzz/expr/corpus/splat-full.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/splat-full.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo.bar[*].baz
|
1
hcl/hclsyntax/fuzz/expr/corpus/utf8.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/utf8.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
föo("föo") + föo
|
1
hcl/hclsyntax/fuzz/expr/corpus/var.hcle
Normal file
1
hcl/hclsyntax/fuzz/expr/corpus/var.hcle
Normal file
@ -0,0 +1 @@
|
|||||||
|
var.bar
|
16
hcl/hclsyntax/fuzz/expr/fuzz.go
Normal file
16
hcl/hclsyntax/fuzz/expr/fuzz.go
Normal 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
|
||||||
|
}
|
0
hcl/hclsyntax/fuzz/template/corpus/empty.tmpl
Normal file
0
hcl/hclsyntax/fuzz/template/corpus/empty.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/escape-dollar.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/escape-dollar.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
hi $${var.foo}
|
1
hcl/hclsyntax/fuzz/template/corpus/escape-newline.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/escape-newline.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo ${"bar\nbaz"}
|
1
hcl/hclsyntax/fuzz/template/corpus/function-call.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/function-call.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
hi ${title(var.name)}
|
1
hcl/hclsyntax/fuzz/template/corpus/int.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/int.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo ${42}
|
1
hcl/hclsyntax/fuzz/template/corpus/just-interp.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/just-interp.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
${var.bar}
|
1
hcl/hclsyntax/fuzz/template/corpus/literal.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/literal.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo
|
1
hcl/hclsyntax/fuzz/template/corpus/utf8.tmpl
Normal file
1
hcl/hclsyntax/fuzz/template/corpus/utf8.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
föo ${föo("föo")}
|
16
hcl/hclsyntax/fuzz/template/fuzz.go
Normal file
16
hcl/hclsyntax/fuzz/template/fuzz.go
Normal 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
|
||||||
|
}
|
1
hcl/hclsyntax/fuzz/traversal/corpus/attr.hclt
Normal file
1
hcl/hclsyntax/fuzz/traversal/corpus/attr.hclt
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo.bar
|
1
hcl/hclsyntax/fuzz/traversal/corpus/complex.hclt
Normal file
1
hcl/hclsyntax/fuzz/traversal/corpus/complex.hclt
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo.bar[1].baz["foo"].pizza
|
1
hcl/hclsyntax/fuzz/traversal/corpus/index.hclt
Normal file
1
hcl/hclsyntax/fuzz/traversal/corpus/index.hclt
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo[1]
|
1
hcl/hclsyntax/fuzz/traversal/corpus/root.hclt
Normal file
1
hcl/hclsyntax/fuzz/traversal/corpus/root.hclt
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo
|
16
hcl/hclsyntax/fuzz/traversal/fuzz.go
Normal file
16
hcl/hclsyntax/fuzz/traversal/fuzz.go
Normal 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user