diff --git a/Makefile b/Makefile index c133d2a..fa49a19 100644 --- a/Makefile +++ b/Makefile @@ -11,13 +11,18 @@ iso_target := $(BUILD_DIR)/kernel-$(ARCH).iso # this: make run GO=go1.8 GO ?= go -# Prepend build path to GOPATH so the compiled packages and linter dependencies +# Prepend build path to GOPATH so the compiled packages and linter dependencies # end up inside the build folder GOPATH := $(BUILD_ABS_DIR):$(shell pwd):$(GOPATH) +FUZZ_PKG_LIST := src/gopheros/device/acpi/aml +# To append more entries to the above list use the following syntax +# FUZZ_PKG_LIST += path-to-pkg + ifeq ($(OS), Linux) export SHELL := /bin/bash -o pipefail + LD := ld AS := nasm @@ -143,7 +148,7 @@ run-vbox: iso VBoxManage storageattach $(VBOX_VM_NAME) --storagectl "IDE Controller" --port 0 --device 0 --type dvddrive \ --medium $(iso_target) || true VBoxManage startvm $(VBOX_VM_NAME) - + # When building gdb target disable optimizations (-N) and inlining (l) of Go code gdb: GC_FLAGS += -N -l gdb: iso @@ -185,12 +190,28 @@ lint: lint-check-deps src/... lint-check-deps: + @echo [go get] installing linter dependencies @GOPATH=$(GOPATH) $(GO) get -u -t gopkg.in/alecthomas/gometalinter.v1 @GOPATH=$(GOPATH) PATH=$(BUILD_ABS_DIR)/bin:$(PATH) gometalinter.v1 --install >/dev/null test: GOPATH=$(GOPATH) $(GO) test -cover gopheros/... +fuzz-deps: + @mkdir -p $(BUILD_DIR)/fuzz + @echo [go get] installing go-fuzz dependencies + @GOPATH=$(GOPATH) $(GO) get -u github.com/dvyukov/go-fuzz/... + +%.fuzzpkg: % + @echo [go-fuzz] fuzzing: $< + @GOPATH=$(GOPATH) PATH=$(BUILD_ABS_DIR)/bin:$(PATH) go-fuzz-build -o $(BUILD_ABS_DIR)/fuzz/$(subst /,_,$<).zip $(subst src/,,$<) + @mkdir -p $(BUILD_ABS_DIR)/fuzz/corpus/$(subst /,_,$<)/corpus + @echo [go-fuzz] + grepping for corpus file hints in $< + @grep "go-fuzz-corpus+=" $&1 | sed -e "s/^/ | /g" + +test-fuzz: fuzz-deps $(addsuffix .fuzzpkg,$(FUZZ_PKG_LIST)) + collect-coverage: GOPATH=$(GOPATH) sh coverage.sh diff --git a/src/gopheros/device/acpi/aml/parser_fuzz.go b/src/gopheros/device/acpi/aml/parser_fuzz.go new file mode 100644 index 0000000..6d3822f --- /dev/null +++ b/src/gopheros/device/acpi/aml/parser_fuzz.go @@ -0,0 +1,40 @@ +// +build gofuzz +// +// The following lines contain paths to interesting corpus data and will be +// automatically grepped and copied by the Makefile when fuzzing. +// +//go-fuzz-corpus+=src/gopheros/device/acpi/table/tabletest/DSDT.aml +//go-fuzz-corpus+=src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml + +package aml + +import ( + "gopheros/device/acpi/table" + "io/ioutil" + "unsafe" +) + +// Fuzz is the driver for go-fuzz. The function must return 1 if the fuzzer +// should increase priority of the given input during subsequent fuzzing (for +// example, the input is lexically correct and was parsed successfully); -1 if +// the input must not be added to corpus even if gives new coverage; and 0 +// otherwise; other values are reserved for future use. +func Fuzz(data []byte) int { + // Setup SDT header pointing to data + headerLen := unsafe.Sizeof(table.SDTHeader{}) + stream := make([]byte, int(headerLen)+len(data)) + copy(stream[headerLen:], data) + + header := (*table.SDTHeader)(unsafe.Pointer(&stream[0])) + header.Signature = [4]byte{'D', 'S', 'D', 'T'} + header.Length = uint32(len(stream)) + header.Revision = 2 + + tree := NewObjectTree() + tree.CreateDefaultScopes(0) + if err := NewParser(ioutil.Discard, tree).ParseAML(uint8(1), "DSDT", header); err != nil { + return 0 + } + + return 1 +} diff --git a/src/gopheros/device/acpi/aml/parser_test.go b/src/gopheros/device/acpi/aml/parser_test.go index 29b9721..9fab368 100644 --- a/src/gopheros/device/acpi/aml/parser_test.go +++ b/src/gopheros/device/acpi/aml/parser_test.go @@ -16,9 +16,41 @@ import ( ) var ( - regenExpFiles = flag.Bool("aml-regenerate-parser-exp-files", false, "Regenerate the expected output files for AML parser tests against real AML files") + regenExpFiles = flag.Bool("aml-regenerate-parser-exp-files", false, "Regenerate the expected output files for AML parser tests against real AML files") + replayCrashersFrom = flag.String("aml-replay-crashers-from", "", "Replay go-fuzz generated crasher files from this folder") ) +// TestParserCrashers scans through the crasher corpus generated by go-fuzz and +// pipes each corpus through the AML parser. +func TestParserCrashers(t *testing.T) { + if *replayCrashersFrom == "" { + t.Skip("-aml-replay-crashers-from not specified; skipping") + return + } + + fuzzFiles, err := filepath.Glob(filepath.Join(*replayCrashersFrom, "*")) + if err != nil { + t.Fatal(err) + } + + for _, fuzzFile := range fuzzFiles { + // corpus files lack an extension + if filepath.Ext(fuzzFile) != "" { + continue + } + + data, err := ioutil.ReadFile(fuzzFile) + if err != nil { + t.Fatal(err) + } + + t.Logf("trying to parse crash corpus: %q", fuzzFile) + p, resolver := parserForMockPayload(t, data) + _ = p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")) + } + +} + func TestParser(t *testing.T) { flag.Parse()