diff --git a/.circleci/config.yml b/.circleci/config.yml index 5e830c0..bad8ba0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,10 +1,23 @@ version: 2.1 -jobs: - build: + +executors: + # Whenever the Go version is updated here, .travis.yml and .promu.yml + # should also be updated. + golang: docker: - - image: "circleci/golang:1.12" - environment: + - image: circleci/golang:1.12 + environment: GO111MODULE: "on" + + fuzzit: + docker: + - image: fuzzitdev/golang:1.12.7-buster + environment: + GO111MODULE: "off" + +jobs: + test: + executor: golang working_directory: /go/src/github.com/marten-seemann/qpack steps: - checkout @@ -29,3 +42,19 @@ jobs: command: | cat `find . -name "*.coverprofile"` > coverage.txt bash <(curl -s https://codecov.io/bash) -f coverage.txt + fuzzit: + executor: fuzzit + working_directory: /go/src/github.com/marten-seemann/qpack + steps: + - checkout + - setup_remote_docker + - run: go get ./... + - run: .circleci/fuzzit.sh + +workflows: + version: 2 + qpack: + jobs: + - test + - fuzzit + \ No newline at end of file diff --git a/.circleci/fuzzit.sh b/.circleci/fuzzit.sh new file mode 100755 index 0000000..72740f1 --- /dev/null +++ b/.circleci/fuzzit.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -ex + +if [ "$CIRCLE_PULL_REQUEST" = "false" ]; then + export FUZZING_TYPE="fuzzing" + export BRANCH=${CIRCLE_BRANCH} +else + export FUZZING_TYPE="local-regression" + export BRANCH="PR-${CIRCLE_PULL_REQUEST}" +fi + +## Install fuzzit +wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.44/fuzzit_Linux_x86_64 +chmod a+x fuzzit + +## Install go-fuzz +go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build + +cd fuzzing +go-fuzz-build -libfuzzer -o fuzz-qpack.a . +clang-9 -fsanitize=fuzzer fuzz-qpack.a -o fuzz-qpack +cd .. + +# Create the jobs +./fuzzit create job --type ${FUZZING_TYPE} --branch ${BRANCH} --revision=${CIRCLE_SHA1} quic-go/fuzz-header fuzzing/fuzz-qpack diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66c189a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +fuzzing/*.zip +fuzzing/coverprofile +fuzzing/crashers +fuzzing/sonarprofile +fuzzing/suppressions +fuzzing/corpus/ diff --git a/fuzzing/fuzz.go b/fuzzing/fuzz.go new file mode 100644 index 0000000..bfcaa69 --- /dev/null +++ b/fuzzing/fuzz.go @@ -0,0 +1,76 @@ +// +build gofuzz + +package qpack + +import ( + "bytes" + "fmt" + "reflect" + + "github.com/marten-seemann/qpack" +) + +func Fuzz(data []byte) int { + if len(data) < 1 { + return 0 + } + + chunkLen := int(data[0]) + 1 + data = data[1:] + + fields, err := qpack.NewDecoder(nil).DecodeFull(data) + if err != nil { + return 0 + } + if len(fields) == 0 { + return 0 + } + + var writtenFields []qpack.HeaderField + decoder := qpack.NewDecoder(func(hf qpack.HeaderField) { + writtenFields = append(writtenFields, hf) + }) + for len(data) > 0 { + var chunk []byte + if chunkLen <= len(data) { + chunk = data[:chunkLen] + data = data[chunkLen:] + } else { + chunk = data + data = nil + } + n, err := decoder.Write(chunk) + if err != nil { + return 0 + } + if n != len(chunk) { + panic("len error") + } + } + if !reflect.DeepEqual(fields, writtenFields) { + fmt.Printf("%#v vs %#v", fields, writtenFields) + panic("Write() and DecodeFull() produced different results") + } + + buf := &bytes.Buffer{} + encoder := qpack.NewEncoder(buf) + for _, hf := range fields { + if err := encoder.WriteField(hf); err != nil { + panic(err) + } + } + if err := encoder.Close(); err != nil { + panic(err) + } + + encodedFields, err := qpack.NewDecoder(nil).DecodeFull(buf.Bytes()) + if err != nil { + fmt.Printf("Fields: %#v\n", fields) + panic(err) + } + if !reflect.DeepEqual(fields, encodedFields) { + fmt.Printf("%#v vs %#v", fields, encodedFields) + panic("unequal") + } + return 0 +}