From 087626e693fde0ce529e6e10aa6946ef9f155f10 Mon Sep 17 00:00:00 2001 From: AdamKorcz Date: Mon, 25 Jul 2022 12:36:26 +0100 Subject: [PATCH 1/8] pkg/util: Add ClusterfuzzLite Signed-off-by: AdamKorcz --- .clusterfuzzlite/Dockerfile | 5 +++++ .clusterfuzzlite/build.sh | 1 + .clusterfuzzlite/project.yaml | 1 + .github/workflows/cflite_pr.yml | 29 +++++++++++++++++++++++++++++ pkg/util/fuzz.go | 28 ++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 .clusterfuzzlite/Dockerfile create mode 100644 .clusterfuzzlite/build.sh create mode 100644 .clusterfuzzlite/project.yaml create mode 100644 .github/workflows/cflite_pr.yml create mode 100644 pkg/util/fuzz.go diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 00000000..82fadacb --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,5 @@ +FROM gcr.io/oss-fuzz-base/base-builder-go +RUN git clone --depth 1 https://github.com/containrrr/shoutrrr +COPY . $SRC/shoutrrr +WORKDIR $SRC/shoutrrr +COPY ./clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100644 index 00000000..d1f91ac8 --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1 @@ +compile_go_fuzzer github.com/containrrr/shoutrrr/pkg/util FuzzPartitionMessage fuzz_partition_message diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 00000000..eb93f278 --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: go \ No newline at end of file diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml new file mode 100644 index 00000000..080859c1 --- /dev/null +++ b/.github/workflows/cflite_pr.yml @@ -0,0 +1,29 @@ +name: ClusterFuzzLite PR fuzzing +on: + workflow_dispatch: + pull_request: + paths: + - '**' +permissions: read-all +jobs: + PR: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sanitizer: [address] + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + sanitizer: ${{ matrix.sanitizer }} + language: go + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 400 + mode: 'code-change' + sanitizer: ${{ matrix.sanitizer }} diff --git a/pkg/util/fuzz.go b/pkg/util/fuzz.go new file mode 100644 index 00000000..6f071bbb --- /dev/null +++ b/pkg/util/fuzz.go @@ -0,0 +1,28 @@ +package util + +import ( + fuzz "github.com/AdaLogics/go-fuzz-headers" + t "github.com/containrrr/shoutrrr/pkg/types" +) + +func FuzzPartitionMessage(data []byte) int { + f := fuzz.NewConsumer(data) + + input, err := f.GetString() + if err != nil { + return 0 + } + + limits := t.MessageLimit{} + err = f.GenerateStruct(&limits) + if err != nil { + return 0 + } + + distance, err := f.GetInt() + if err != nil { + return 0 + } + _, _ = PartitionMessage(input, limits, distance) + return 1 +} From 0134e0fc6386970f8b672bb7c30bed7d0a7c50b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Tue, 26 Jul 2022 16:56:28 +0200 Subject: [PATCH 2/8] fix ci and code coverage --- .clusterfuzzlite/Dockerfile | 2 +- .clusterfuzzlite/build.sh | 2 +- pkg/util/{ => fuzz}/fuzz.go | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) rename pkg/util/{ => fuzz}/fuzz.go (73%) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 82fadacb..67ceb927 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -2,4 +2,4 @@ FROM gcr.io/oss-fuzz-base/base-builder-go RUN git clone --depth 1 https://github.com/containrrr/shoutrrr COPY . $SRC/shoutrrr WORKDIR $SRC/shoutrrr -COPY ./clusterfuzzlite/build.sh $SRC/ +COPY ./.clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index d1f91ac8..4ce67db1 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -1 +1 @@ -compile_go_fuzzer github.com/containrrr/shoutrrr/pkg/util FuzzPartitionMessage fuzz_partition_message +compile_go_fuzzer github.com/containrrr/shoutrrr/pkg/util/fuzz FuzzPartitionMessage fuzz_partition_message diff --git a/pkg/util/fuzz.go b/pkg/util/fuzz/fuzz.go similarity index 73% rename from pkg/util/fuzz.go rename to pkg/util/fuzz/fuzz.go index 6f071bbb..3214c795 100644 --- a/pkg/util/fuzz.go +++ b/pkg/util/fuzz/fuzz.go @@ -1,8 +1,12 @@ -package util +//go:build gofuzz +// +build gofuzz + +package fuzz import ( fuzz "github.com/AdaLogics/go-fuzz-headers" t "github.com/containrrr/shoutrrr/pkg/types" + "github.com/containrrr/shoutrrr/pkg/util" ) func FuzzPartitionMessage(data []byte) int { @@ -23,6 +27,6 @@ func FuzzPartitionMessage(data []byte) int { if err != nil { return 0 } - _, _ = PartitionMessage(input, limits, distance) + _, _ = util.PartitionMessage(input, limits, distance) return 1 } From f4e95b8a5c74d4e384dbe44477f2392507fde281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Tue, 26 Jul 2022 17:26:39 +0200 Subject: [PATCH 3/8] make limits unsigned to avoid fuzzing negative limits --- pkg/types/message_limit.go | 6 +++--- pkg/util/partition_message.go | 12 ++++++------ pkg/util/partition_message_test.go | 22 +++++++++++----------- pkg/util/util.go | 16 ++++++++++++++++ 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/pkg/types/message_limit.go b/pkg/types/message_limit.go index 238c441b..32b0a719 100644 --- a/pkg/types/message_limit.go +++ b/pkg/types/message_limit.go @@ -2,9 +2,9 @@ package types // MessageLimit is used for declaring the payload limits for services upstream APIs type MessageLimit struct { - ChunkSize int - TotalChunkSize int + ChunkSize uint + TotalChunkSize uint // Maximum number of chunks (including the last chunk for meta data) - ChunkCount int + ChunkCount uint } diff --git a/pkg/util/partition_message.go b/pkg/util/partition_message.go index 6f47dc23..acf81616 100644 --- a/pkg/util/partition_message.go +++ b/pkg/util/partition_message.go @@ -15,8 +15,8 @@ const ellipsis = " [...]" func PartitionMessage(input string, limits t.MessageLimit, distance int) (items []t.MessageItem, omitted int) { runes := []rune(input) chunkOffset := 0 - maxTotal := Min(len(runes), limits.TotalChunkSize) - maxCount := limits.ChunkCount - 1 + maxTotal := Min(len(runes), int(limits.TotalChunkSize)) + maxCount := int(limits.ChunkCount) - 1 if len(input) == 0 { // If the message is empty, return an empty array @@ -26,7 +26,7 @@ func PartitionMessage(input string, limits t.MessageLimit, distance int) (items for i := 0; i < maxCount; i++ { // If no suitable split point is found, use the chunkSize - chunkEnd := chunkOffset + limits.ChunkSize + chunkEnd := chunkOffset + int(limits.ChunkSize) // ... and start next chunk directly after this one nextChunkStart := chunkEnd if chunkEnd >= maxTotal { @@ -69,7 +69,7 @@ func Ellipsis(text string, maxLength int) string { // MessageItemsFromLines creates a set of MessageItems that is compatible with the supplied limits func MessageItemsFromLines(plain string, limits t.MessageLimit) (batches [][]t.MessageItem) { - maxCount := limits.ChunkCount + maxCount := int(limits.ChunkCount) lines := strings.Split(plain, "\n") batches = make([][]t.MessageItem, 0) @@ -78,9 +78,9 @@ func MessageItemsFromLines(plain string, limits t.MessageLimit) (batches [][]t.M totalLength := 0 for _, line := range lines { - maxLen := limits.ChunkSize + maxLen := int(limits.ChunkSize) - if len(items) == maxCount || totalLength+maxLen > limits.TotalChunkSize { + if len(items) == maxCount || totalLength+maxLen > int(limits.TotalChunkSize) { batches = append(batches, items) items = items[:0] } diff --git a/pkg/util/partition_message_test.go b/pkg/util/partition_message_test.go index 8f678aad..d8c50c23 100644 --- a/pkg/util/partition_message_test.go +++ b/pkg/util/partition_message_test.go @@ -61,7 +61,7 @@ var _ = Describe("Partition Message", func() { } testString := "" - for inputLen := 1; inputLen < 8000; inputLen++ { + for inputLen := uint(1); inputLen < 8000; inputLen++ { // add a rune to the string using a repeatable pattern (single digit hex of position) testString += strconv.FormatInt(int64(inputLen%16), 16) items, omitted := PartitionMessage(testString, unalignedLimits, 7) @@ -74,7 +74,7 @@ var _ = Describe("Partition Message", func() { // the chunk size is the remainder of, the total size, // or the max size, whatever is smallest, // and the previous chunk sizes - chunkSize := Min(inputLen, unalignedLimits.TotalChunkSize) % unalignedLimits.ChunkSize + chunkSize := UMin(inputLen, unalignedLimits.TotalChunkSize) % unalignedLimits.ChunkSize // if the "rest" of the runes needs another chunk if chunkSize > 0 { // expect the chunk to contain the "rest" of the runes @@ -87,17 +87,17 @@ var _ = Describe("Partition Message", func() { if ii == len(items)-1 { for ri, r := range item.Text { runeOffset := (len(item.Text) - ri) - 1 - runeVal, err := strconv.ParseInt(string(r), 16, 64) - expectedLen := Min(inputLen, unalignedLimits.TotalChunkSize) - expectedVal := (expectedLen - runeOffset) % 16 + runeVal, err := strconv.ParseUint(string(r), 16, 64) + expectedLen := UMin(inputLen, unalignedLimits.TotalChunkSize) + expectedVal := (expectedLen - uint(runeOffset)) % 16 Expect(err).ToNot(HaveOccurred()) - Expect(runeVal).To(Equal(int64(expectedVal))) + Expect(runeVal).To(Equal(uint64(expectedVal))) } } included += len(item.Text) - Expect(item.Text).To(HaveLen(expectedSize)) + Expect(item.Text).To(HaveLen(int(expectedSize))) } Expect(omitted + included).To(Equal(inputLen)) @@ -161,17 +161,17 @@ func testMessageItemsFromLines(hundreds int, limits types.MessageLimit, repeat i return } -func testPartitionMessage(hundreds int, limits types.MessageLimit, distance int) (items []types.MessageItem, omitted int) { +func testPartitionMessage(hundreds uint, limits types.MessageLimit, distance int) (items []types.MessageItem, omitted int) { builder := strings.Builder{} - for i := 0; i < hundreds; i++ { + for i := uint(0); i < hundreds; i++ { builder.WriteString(hundredChars) } items, omitted = PartitionMessage(builder.String(), limits, distance) - contentSize := Min(hundreds*100, limits.TotalChunkSize) - expectedOmitted := Max(0, (hundreds*100)-contentSize) + contentSize := UMin(hundreds*100, limits.TotalChunkSize) + expectedOmitted := UMax(0, (hundreds*100)-contentSize) ExpectWithOffset(0, omitted).To(Equal(expectedOmitted)) diff --git a/pkg/util/util.go b/pkg/util/util.go index 1e8cba49..f06d680c 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -13,6 +13,14 @@ func Min(a int, b int) int { return b } +// UMin returns the smallest of a and b +func UMin(a uint, b uint) uint { + if a < b { + return a + } + return b +} + // Max returns the largest of a and b func Max(a int, b int) int { if a > b { @@ -21,5 +29,13 @@ func Max(a int, b int) int { return b } +// UMax returns the largest of a and b +func UMax(a uint, b uint) uint { + if a > b { + return a + } + return b +} + // DiscardLogger is a logger that discards any output written to it var DiscardLogger = log.New(ioutil.Discard, "", 0) From dfd6ae31191b090ba06130ae53dd2b0361e05a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Tue, 26 Jul 2022 17:34:08 +0200 Subject: [PATCH 4/8] relax sign requirement in tests --- pkg/util/partition_message_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/util/partition_message_test.go b/pkg/util/partition_message_test.go index d8c50c23..31e0f643 100644 --- a/pkg/util/partition_message_test.go +++ b/pkg/util/partition_message_test.go @@ -92,14 +92,14 @@ var _ = Describe("Partition Message", func() { expectedVal := (expectedLen - uint(runeOffset)) % 16 Expect(err).ToNot(HaveOccurred()) - Expect(runeVal).To(Equal(uint64(expectedVal))) + Expect(runeVal).To(BeEquivalentTo(expectedVal)) } } included += len(item.Text) Expect(item.Text).To(HaveLen(int(expectedSize))) } - Expect(omitted + included).To(Equal(inputLen)) + Expect(omitted + included).To(BeEquivalentTo(inputLen)) } }) @@ -133,8 +133,8 @@ var _ = Describe("Partition Message", func() { batches := testMessageItemsFromLines(hundreds, limits, repeat) items := batches[0] - Expect(len(items[0].Text)).To(Equal(limits.ChunkSize)) - Expect(len(items[1].Text)).To(Equal(limits.ChunkSize)) + Expect(len(items[0].Text)).To(BeEquivalentTo(limits.ChunkSize)) + Expect(len(items[1].Text)).To(BeEquivalentTo(limits.ChunkSize)) }) }) }) @@ -173,7 +173,7 @@ func testPartitionMessage(hundreds uint, limits types.MessageLimit, distance int contentSize := UMin(hundreds*100, limits.TotalChunkSize) expectedOmitted := UMax(0, (hundreds*100)-contentSize) - ExpectWithOffset(0, omitted).To(Equal(expectedOmitted)) + ExpectWithOffset(0, omitted).To(BeEquivalentTo(expectedOmitted)) return } From 4b08bf18284b0ff6135a451585671ef62def97a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Tue, 26 Jul 2022 17:57:06 +0200 Subject: [PATCH 5/8] simplify sign casts --- pkg/util/partition_message_test.go | 18 +++++++++--------- pkg/util/util.go | 16 ---------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/pkg/util/partition_message_test.go b/pkg/util/partition_message_test.go index 31e0f643..5ce1015e 100644 --- a/pkg/util/partition_message_test.go +++ b/pkg/util/partition_message_test.go @@ -61,20 +61,20 @@ var _ = Describe("Partition Message", func() { } testString := "" - for inputLen := uint(1); inputLen < 8000; inputLen++ { + for inputLen := 1; inputLen < 8000; inputLen++ { // add a rune to the string using a repeatable pattern (single digit hex of position) testString += strconv.FormatInt(int64(inputLen%16), 16) items, omitted := PartitionMessage(testString, unalignedLimits, 7) included := 0 for ii, item := range items { - expectedSize := unalignedLimits.ChunkSize + expectedSize := int(unalignedLimits.ChunkSize) // The last chunk might be smaller than the preceeding chunks if ii == len(items)-1 { // the chunk size is the remainder of, the total size, // or the max size, whatever is smallest, // and the previous chunk sizes - chunkSize := UMin(inputLen, unalignedLimits.TotalChunkSize) % unalignedLimits.ChunkSize + chunkSize := Min(inputLen, int(unalignedLimits.TotalChunkSize)) % int(unalignedLimits.ChunkSize) // if the "rest" of the runes needs another chunk if chunkSize > 0 { // expect the chunk to contain the "rest" of the runes @@ -88,8 +88,8 @@ var _ = Describe("Partition Message", func() { for ri, r := range item.Text { runeOffset := (len(item.Text) - ri) - 1 runeVal, err := strconv.ParseUint(string(r), 16, 64) - expectedLen := UMin(inputLen, unalignedLimits.TotalChunkSize) - expectedVal := (expectedLen - uint(runeOffset)) % 16 + expectedLen := Min(inputLen, int(unalignedLimits.TotalChunkSize)) + expectedVal := (expectedLen - runeOffset) % 16 Expect(err).ToNot(HaveOccurred()) Expect(runeVal).To(BeEquivalentTo(expectedVal)) @@ -161,17 +161,17 @@ func testMessageItemsFromLines(hundreds int, limits types.MessageLimit, repeat i return } -func testPartitionMessage(hundreds uint, limits types.MessageLimit, distance int) (items []types.MessageItem, omitted int) { +func testPartitionMessage(hundreds int, limits types.MessageLimit, distance int) (items []types.MessageItem, omitted int) { builder := strings.Builder{} - for i := uint(0); i < hundreds; i++ { + for i := 0; i < hundreds; i++ { builder.WriteString(hundredChars) } items, omitted = PartitionMessage(builder.String(), limits, distance) - contentSize := UMin(hundreds*100, limits.TotalChunkSize) - expectedOmitted := UMax(0, (hundreds*100)-contentSize) + contentSize := Min(hundreds*100, int(limits.TotalChunkSize)) + expectedOmitted := Max(0, (hundreds*100)-contentSize) ExpectWithOffset(0, omitted).To(BeEquivalentTo(expectedOmitted)) diff --git a/pkg/util/util.go b/pkg/util/util.go index f06d680c..1e8cba49 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -13,14 +13,6 @@ func Min(a int, b int) int { return b } -// UMin returns the smallest of a and b -func UMin(a uint, b uint) uint { - if a < b { - return a - } - return b -} - // Max returns the largest of a and b func Max(a int, b int) int { if a > b { @@ -29,13 +21,5 @@ func Max(a int, b int) int { return b } -// UMax returns the largest of a and b -func UMax(a uint, b uint) uint { - if a > b { - return a - } - return b -} - // DiscardLogger is a logger that discards any output written to it var DiscardLogger = log.New(ioutil.Discard, "", 0) From 896904f304d2b2205167c90971edd5522d71bb22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20ma=CC=8Ase=CC=81n?= Date: Sat, 30 Jul 2022 11:03:24 +0200 Subject: [PATCH 6/8] lint fixes --- .clusterfuzzlite/Dockerfile | 2 +- .clusterfuzzlite/build.sh | 2 ++ pkg/util/fuzz/fuzz.go | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 67ceb927..1ed42cfe 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go +FROM gcr.io/oss-fuzz-base/base-builder-go:v1 RUN git clone --depth 1 https://github.com/containrrr/shoutrrr COPY . $SRC/shoutrrr WORKDIR $SRC/shoutrrr diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 4ce67db1..1316b58d 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -1 +1,3 @@ +#!/binsh + compile_go_fuzzer github.com/containrrr/shoutrrr/pkg/util/fuzz FuzzPartitionMessage fuzz_partition_message diff --git a/pkg/util/fuzz/fuzz.go b/pkg/util/fuzz/fuzz.go index 3214c795..f1ff62fa 100644 --- a/pkg/util/fuzz/fuzz.go +++ b/pkg/util/fuzz/fuzz.go @@ -9,6 +9,7 @@ import ( "github.com/containrrr/shoutrrr/pkg/util" ) +// FuzzPartitionMessage fuzzes the util.PartitionMessage function func FuzzPartitionMessage(data []byte) int { f := fuzz.NewConsumer(data) From 7ccc21f136fd7806adfe48ba32eb0d7067ea119d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20ma=CC=8Ase=CC=81n?= Date: Sat, 30 Jul 2022 11:51:23 +0200 Subject: [PATCH 7/8] add go build hack to fix lint --- pkg/util/fuzz/fuzz.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/util/fuzz/fuzz.go b/pkg/util/fuzz/fuzz.go index f1ff62fa..f42099a6 100644 --- a/pkg/util/fuzz/fuzz.go +++ b/pkg/util/fuzz/fuzz.go @@ -1,5 +1,7 @@ -//go:build gofuzz -// +build gofuzz +//go:build gofuzz || cgo +// +build gofuzz cgo +// Note that the `cgo` above is a hack to prevent the file from being built in releases, but still +// included when checking for doc comments (lint) package fuzz From 996a74e5d0381ebc73de2e34eb9c3a3a3905144f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20ma=CC=8Ase=CC=81n?= Date: Sat, 30 Jul 2022 12:06:04 +0200 Subject: [PATCH 8/8] move and ignore fuzzers in codacy --- .clusterfuzzlite/build.sh | 2 +- .codacy.yml | 3 +++ pkg/util/fuzz/fuzz.go => fuzz/partition_message.go | 6 ++---- 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 .codacy.yml rename pkg/util/fuzz/fuzz.go => fuzz/partition_message.go (74%) diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 1316b58d..92c768ba 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -1,3 +1,3 @@ #!/binsh -compile_go_fuzzer github.com/containrrr/shoutrrr/pkg/util/fuzz FuzzPartitionMessage fuzz_partition_message +compile_go_fuzzer github.com/containrrr/shoutrrr/fuzz FuzzPartitionMessage fuzz_partition_message diff --git a/.codacy.yml b/.codacy.yml new file mode 100644 index 00000000..27aad9cd --- /dev/null +++ b/.codacy.yml @@ -0,0 +1,3 @@ +--- +exclude_paths: + - "fuzz/*" \ No newline at end of file diff --git a/pkg/util/fuzz/fuzz.go b/fuzz/partition_message.go similarity index 74% rename from pkg/util/fuzz/fuzz.go rename to fuzz/partition_message.go index f42099a6..f1ff62fa 100644 --- a/pkg/util/fuzz/fuzz.go +++ b/fuzz/partition_message.go @@ -1,7 +1,5 @@ -//go:build gofuzz || cgo -// +build gofuzz cgo -// Note that the `cgo` above is a hack to prevent the file from being built in releases, but still -// included when checking for doc comments (lint) +//go:build gofuzz +// +build gofuzz package fuzz