diff --git a/hclwrite/fuzz/.gitignore b/hclwrite/fuzz/.gitignore
new file mode 100644
index 0000000..9490851
--- /dev/null
+++ b/hclwrite/fuzz/.gitignore
@@ -0,0 +1 @@
+fuzz*-fuzz.zip
diff --git a/hclwrite/fuzz/Makefile b/hclwrite/fuzz/Makefile
new file mode 100644
index 0000000..e303748
--- /dev/null
+++ b/hclwrite/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 -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
diff --git a/hclwrite/fuzz/README.md b/hclwrite/fuzz/README.md
new file mode 100644
index 0000000..0e11623
--- /dev/null
+++ b/hclwrite/fuzz/README.md
@@ -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://$(($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
+```
diff --git a/hclwrite/fuzz/config/corpus/attr-expr.hcl b/hclwrite/fuzz/config/corpus/attr-expr.hcl
new file mode 100644
index 0000000..908bbc9
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/attr-expr.hcl
@@ -0,0 +1 @@
+foo = upper(bar + baz[1])
diff --git a/hclwrite/fuzz/config/corpus/attr-literal.hcl b/hclwrite/fuzz/config/corpus/attr-literal.hcl
new file mode 100644
index 0000000..5abc475
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/attr-literal.hcl
@@ -0,0 +1 @@
+foo = "bar"
diff --git a/hclwrite/fuzz/config/corpus/attr.hcl b/hclwrite/fuzz/config/corpus/attr.hcl
new file mode 100644
index 0000000..6c11db6
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/attr.hcl
@@ -0,0 +1 @@
+a = foo.bar
diff --git a/hclwrite/fuzz/config/corpus/block-attrs.hcl b/hclwrite/fuzz/config/corpus/block-attrs.hcl
new file mode 100644
index 0000000..eb529ef
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/block-attrs.hcl
@@ -0,0 +1,3 @@
+block {
+  foo = true
+}
diff --git a/hclwrite/fuzz/config/corpus/block-comment.hcl b/hclwrite/fuzz/config/corpus/block-comment.hcl
new file mode 100644
index 0000000..9ed4c68
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/block-comment.hcl
@@ -0,0 +1,4 @@
+/* multi
+   line
+   comment
+*/
diff --git a/hclwrite/fuzz/config/corpus/block-empty.hcl b/hclwrite/fuzz/config/corpus/block-empty.hcl
new file mode 100644
index 0000000..b3b13a1
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/block-empty.hcl
@@ -0,0 +1,2 @@
+block {
+}
diff --git a/hclwrite/fuzz/config/corpus/block-nested.hcl b/hclwrite/fuzz/config/corpus/block-nested.hcl
new file mode 100644
index 0000000..304183a
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/block-nested.hcl
@@ -0,0 +1,5 @@
+block {
+  another_block {
+    foo = bar
+  }
+}
diff --git a/hclwrite/fuzz/config/corpus/complex.hcl b/hclwrite/fuzz/config/corpus/complex.hcl
new file mode 100644
index 0000000..00a4a27
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/complex.hcl
@@ -0,0 +1 @@
+a = foo.bar[1].baz["foo"].pizza
diff --git a/hclwrite/fuzz/config/corpus/empty.hcl b/hclwrite/fuzz/config/corpus/empty.hcl
new file mode 100644
index 0000000..e69de29
diff --git a/hclwrite/fuzz/config/corpus/escape-dollar.hcl b/hclwrite/fuzz/config/corpus/escape-dollar.hcl
new file mode 100644
index 0000000..01811cd
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/escape-dollar.hcl
@@ -0,0 +1 @@
+a = "hi $${var.foo}"
diff --git a/hclwrite/fuzz/config/corpus/escape-newline.hcl b/hclwrite/fuzz/config/corpus/escape-newline.hcl
new file mode 100644
index 0000000..2055548
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/escape-newline.hcl
@@ -0,0 +1 @@
+a = "bar\nbaz"
diff --git a/hclwrite/fuzz/config/corpus/function-call-tmpl.hcl b/hclwrite/fuzz/config/corpus/function-call-tmpl.hcl
new file mode 100644
index 0000000..f641c7a
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/function-call-tmpl.hcl
@@ -0,0 +1 @@
+a = "b ${title(var.name)}
diff --git a/hclwrite/fuzz/config/corpus/function-call.hcl b/hclwrite/fuzz/config/corpus/function-call.hcl
new file mode 100644
index 0000000..f3cafb2
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/function-call.hcl
@@ -0,0 +1 @@
+a = title(var.name)
diff --git a/hclwrite/fuzz/config/corpus/hash-comment.hcl b/hclwrite/fuzz/config/corpus/hash-comment.hcl
new file mode 100644
index 0000000..9d18bcb
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/hash-comment.hcl
@@ -0,0 +1 @@
+# another comment
diff --git a/hclwrite/fuzz/config/corpus/index.hcl b/hclwrite/fuzz/config/corpus/index.hcl
new file mode 100644
index 0000000..a22dd44
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/index.hcl
@@ -0,0 +1 @@
+a = foo[1]
diff --git a/hclwrite/fuzz/config/corpus/int-tmpl.hcl b/hclwrite/fuzz/config/corpus/int-tmpl.hcl
new file mode 100644
index 0000000..57e94d3
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/int-tmpl.hcl
@@ -0,0 +1 @@
+a = "foo ${42}"
diff --git a/hclwrite/fuzz/config/corpus/int.hcl b/hclwrite/fuzz/config/corpus/int.hcl
new file mode 100644
index 0000000..aec1027
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/int.hcl
@@ -0,0 +1 @@
+a = 42
diff --git a/hclwrite/fuzz/config/corpus/just-interp.hcl b/hclwrite/fuzz/config/corpus/just-interp.hcl
new file mode 100644
index 0000000..018d5ed
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/just-interp.hcl
@@ -0,0 +1 @@
+a = "${var.bar}"
diff --git a/hclwrite/fuzz/config/corpus/literal.hcl b/hclwrite/fuzz/config/corpus/literal.hcl
new file mode 100644
index 0000000..5ae5461
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/literal.hcl
@@ -0,0 +1 @@
+a = foo
diff --git a/hclwrite/fuzz/config/corpus/lots-of-comments.hcl b/hclwrite/fuzz/config/corpus/lots-of-comments.hcl
new file mode 100644
index 0000000..511f085
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/lots-of-comments.hcl
@@ -0,0 +1,14 @@
+// comment
+block {
+  // another comment
+  another_block { # comment
+    // comment
+    foo = bar
+  }
+
+  /* commented out block
+  blah {
+    bar = foo
+  }
+  */
+}
diff --git a/hclwrite/fuzz/config/corpus/slash-comment.hcl b/hclwrite/fuzz/config/corpus/slash-comment.hcl
new file mode 100644
index 0000000..fef83a9
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/slash-comment.hcl
@@ -0,0 +1 @@
+// comment
diff --git a/hclwrite/fuzz/config/corpus/splat-attr.hcl b/hclwrite/fuzz/config/corpus/splat-attr.hcl
new file mode 100644
index 0000000..38cfe14
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/splat-attr.hcl
@@ -0,0 +1 @@
+a = foo.bar.*.baz
diff --git a/hclwrite/fuzz/config/corpus/splat-dot-full.hcl b/hclwrite/fuzz/config/corpus/splat-dot-full.hcl
new file mode 100644
index 0000000..22e8eb0
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/splat-dot-full.hcl
@@ -0,0 +1 @@
+a = foo.bar.*
diff --git a/hclwrite/fuzz/config/corpus/splat-full.hcl b/hclwrite/fuzz/config/corpus/splat-full.hcl
new file mode 100644
index 0000000..4441191
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/splat-full.hcl
@@ -0,0 +1 @@
+a = foo.bar[*].baz
diff --git a/hclwrite/fuzz/config/corpus/traversal-dot-index-terminal.hcl b/hclwrite/fuzz/config/corpus/traversal-dot-index-terminal.hcl
new file mode 100644
index 0000000..3f3a306
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/traversal-dot-index-terminal.hcl
@@ -0,0 +1 @@
+a = foo.bar.0
diff --git a/hclwrite/fuzz/config/corpus/traversal-dot-index.hcl b/hclwrite/fuzz/config/corpus/traversal-dot-index.hcl
new file mode 100644
index 0000000..31cbf8b
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/traversal-dot-index.hcl
@@ -0,0 +1 @@
+a = foo.bar.4.baz
diff --git a/hclwrite/fuzz/config/corpus/traversal-index.hcl b/hclwrite/fuzz/config/corpus/traversal-index.hcl
new file mode 100644
index 0000000..5523c37
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/traversal-index.hcl
@@ -0,0 +1 @@
+a = foo.bar[4].baz
diff --git a/hclwrite/fuzz/config/corpus/utf8.hcl b/hclwrite/fuzz/config/corpus/utf8.hcl
new file mode 100644
index 0000000..ffad4b0
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/utf8.hcl
@@ -0,0 +1 @@
+foo = "föo ${föo("föo")}"
diff --git a/hclwrite/fuzz/config/corpus/var.hcl b/hclwrite/fuzz/config/corpus/var.hcl
new file mode 100644
index 0000000..62a9b73
--- /dev/null
+++ b/hclwrite/fuzz/config/corpus/var.hcl
@@ -0,0 +1 @@
+a = var.bar
diff --git a/hclwrite/fuzz/config/fuzz.go b/hclwrite/fuzz/config/fuzz.go
new file mode 100644
index 0000000..ea66e67
--- /dev/null
+++ b/hclwrite/fuzz/config/fuzz.go
@@ -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
+}