From 7c18e1e5d6b4ff9a7879b40461fce62e7a979a69 Mon Sep 17 00:00:00 2001 From: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com> Date: Sat, 20 Aug 2022 19:35:10 +0100 Subject: [PATCH] golang: add fuzzers (#8305) Signed-off-by: AdamKorcz Signed-off-by: AdamKorcz --- projects/golang/Dockerfile | 3 + projects/golang/aes_fuzzer.go | 94 ++++++++++++++++++++++++++++++++ projects/golang/build.sh | 18 ++++++ projects/golang/ecdsa_fuzzer.go | 44 +++++++++++++++ projects/golang/regexp_fuzzer.go | 61 +++++++++++++++++++++ projects/golang/x509_fuzzer.go | 55 +++++++++++++++++++ 6 files changed, 275 insertions(+) create mode 100644 projects/golang/aes_fuzzer.go create mode 100644 projects/golang/ecdsa_fuzzer.go create mode 100644 projects/golang/x509_fuzzer.go diff --git a/projects/golang/Dockerfile b/projects/golang/Dockerfile index 48d2f3188..a688cf563 100644 --- a/projects/golang/Dockerfile +++ b/projects/golang/Dockerfile @@ -25,6 +25,9 @@ COPY build.sh text_fuzzer.go \ regexp_fuzzer.go \ language_fuzzer.go \ unicode_fuzzer.go \ + x509_fuzzer.go \ + ecdsa_fuzzer.go \ + aes_fuzzer.go \ elf_fuzzer.go $SRC/ WORKDIR $SRC/golang diff --git a/projects/golang/aes_fuzzer.go b/projects/golang/aes_fuzzer.go new file mode 100644 index 000000000..2f808440c --- /dev/null +++ b/projects/golang/aes_fuzzer.go @@ -0,0 +1,94 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package aes + +import ( + "crypto/aes" + "runtime" + "strings" +) + +func catchPanics() { + if r := recover(); r != nil { + var err string + switch r.(type) { + case string: + err = r.(string) + case runtime.Error: + err = r.(runtime.Error).Error() + case error: + err = r.(error).Error() + } + if strings.Contains(err, "not full block") { + return + } else if strings.Contains(err, "invalid buffer overlap") { + return + } else { + panic(err) + } + } +} + +func FuzzAesCipherDecrypt(data []byte) int { + if len(data) < 5 { + return 0 + } + keyIndex := int(data[0]) + lenIn := int(data[1]) + if lenIn < aes.BlockSize { + return 0 + } + data = data[2:] + + if (keyIndex + 5) > len(data) { + return 0 + } + key := data[:keyIndex] + c, err := aes.NewCipher(key) + if err != nil { + return 0 + } + + dst := make([]byte, lenIn) + src := data[keyIndex+1:] + defer catchPanics() + c.Decrypt(dst, src) + return 1 +} + +func FuzzAesCipherEncrypt(data []byte) int { + if len(data) < 5 { + return 0 + } + keyIndex := int(data[0]) + lenIn := int(data[1]) + data = data[2:] + + if (keyIndex + 5) > len(data) { + return 0 + } + key := data[:keyIndex] + c, err := aes.NewCipher(key) + if err != nil { + return 0 + } + + dst := make([]byte, lenIn) + src := data[keyIndex+1:] + defer catchPanics() + c.Encrypt(dst, src) + return 1 +} diff --git a/projects/golang/build.sh b/projects/golang/build.sh index 0cdb9dffe..fc7407c0b 100755 --- a/projects/golang/build.sh +++ b/projects/golang/build.sh @@ -28,8 +28,24 @@ cd $SRC/golang mkdir text && cp $SRC/text_fuzzer.go ./text/ cp $SRC/language_fuzzer.go ./text/ +mkdir -p crypto/x509 +cp $SRC/x509_fuzzer.go ./crypto/x509/ + +mkdir -p crypto/ecdsa +cp $SRC/ecdsa_fuzzer.go ./crypto/ecdsa/ + +mkdir -p crypto/aes +cp $SRC/aes_fuzzer.go ./crypto/aes/ + go mod init "github.com/dvyukov/go-fuzz-corpus" export FUZZ_ROOT="github.com/dvyukov/go-fuzz-corpus" +compile_go_fuzzer $FUZZ_ROOT/crypto/x509 FuzzParseCert fuzz_parse_cert +zip $OUT/fuzz_parse_cert_seed_corpus.zip $SRC/go/src/crypto/x509/testdata/* +compile_go_fuzzer $FUZZ_ROOT/crypto/x509 FuzzPemDecrypt fuzz_pem_decrypt +zip $OUT/fuzz_pem_decrypt_seed_corpus.zip $SRC/go/src/crypto/x509/testdata/* +compile_go_fuzzer $FUZZ_ROOT/crypto/aes FuzzAesCipherDecrypt fuzz_aes_cipher_decrypt +compile_go_fuzzer $FUZZ_ROOT/crypto/aes FuzzAesCipherEncrypt fuzz_aes_cipher_encrypt +compile_go_fuzzer $FUZZ_ROOT/crypto/ecdsa FuzzEcdsaSign FuzzEcdsaSign compile_go_fuzzer $FUZZ_ROOT/text FuzzAcceptLanguage accept_language_fuzzer compile_go_fuzzer $FUZZ_ROOT/text FuzzMultipleParsers fuzz_multiple_parsers compile_go_fuzzer $FUZZ_ROOT/text FuzzCurrency currency_fuzzer @@ -63,6 +79,8 @@ go mod tidy find . -name "*_test.go" ! -name 'fuzz_test.go' -type f -exec rm -f {} + compile_go_fuzzer regexpPackage FuzzCompile fuzz_regexp_compile compile_go_fuzzer regexpPackage FuzzCompilePOSIX fuzz_compile_posix +compile_go_fuzzer regexpPackage FuzzReplaceAll fuzz_replace_all +compile_go_fuzzer regexpPackage FuzzFindMatchApis fuzz_find_match_apis cd $SRC/go/src/archive/tar go mod init tarPackage diff --git a/projects/golang/ecdsa_fuzzer.go b/projects/golang/ecdsa_fuzzer.go new file mode 100644 index 000000000..5a24c5be7 --- /dev/null +++ b/projects/golang/ecdsa_fuzzer.go @@ -0,0 +1,44 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package ecdsa + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" +) + +func FuzzEcdsaSign(data []byte) int { + if len(data) < 5 { + return 0 + } + c := []elliptic.Curve{ + elliptic.P256(), + elliptic.P224(), + elliptic.P384(), + elliptic.P521(), + } + cIndex := int(data[0] % 4) + firstRandReaderLen := int(data[1]) + data = data[2:] + if (firstRandReaderLen + 3) > len(data) { + return 0 + } + randReader := bytes.NewReader(data[:firstRandReaderLen]) + priv, _ := ecdsa.GenerateKey(c[cIndex], randReader) + _, _, _ = ecdsa.Sign(randReader, priv, data[firstRandReaderLen+1:]) + return 1 +} diff --git a/projects/golang/regexp_fuzzer.go b/projects/golang/regexp_fuzzer.go index d449e29c5..95a355eb6 100644 --- a/projects/golang/regexp_fuzzer.go +++ b/projects/golang/regexp_fuzzer.go @@ -24,3 +24,64 @@ func FuzzCompilePOSIX(data []byte) int { _, _ = CompilePOSIX(string(data)) return 1 } + +func FuzzReplaceAll(data []byte) int { + if len(data) < 5 { + return 0 + } + chunk1Len := int(data[0]) + chunk2Len := int(data[1]) + + if chunk2Len <= chunk1Len || chunk1Len < 3 || len(data) < (chunk2Len+2) { + return 0 + } + + chunk1 := data[2:chunk1Len] + chunk2 := data[chunk1Len+1 : chunk2Len] + chunk3 := data[chunk2Len+1:] + + re, err := Compile(string(chunk1)) + if err != nil { + return 0 + } + + _ = re.ReplaceAll(chunk2, chunk3) + return 1 +} + +func FuzzFindMatchApis(data []byte) int { + if len(data) < 5 { + return 0 + } + callType := int(data[0]) + chunk1Len := int(data[1]) + + data = data[2:] + + if chunk1Len+2 >= len(data) { + return 0 + } + + reString := string(data[:chunk1Len]) + apiPayload := data[chunk1Len+1:] + re, err := Compile(reString) + if err != nil { + return 0 + } + + switch callType % 6 { + case 0: + _ = re.FindIndex(apiPayload) + case 1: + _ = re.FindString(string(apiPayload)) + case 2: + _ = re.FindStringIndex(string(apiPayload)) + case 3: + _ = re.FindSubmatch(apiPayload) + case 4: + _ = re.MatchString(string(apiPayload)) + case 5: + _ = re.Match(apiPayload) + } + return 1 +} diff --git a/projects/golang/x509_fuzzer.go b/projects/golang/x509_fuzzer.go new file mode 100644 index 000000000..1a3a19d96 --- /dev/null +++ b/projects/golang/x509_fuzzer.go @@ -0,0 +1,55 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package x509 + +import ( + "crypto/x509" + "encoding/pem" +) + +func FuzzParseCert(data []byte) int { + if len(data) < 2 { + return 1 + } + plural := int(data[0])%2 == 0 + data = data[1:] + if plural { + _, _ = x509.ParseCertificates(data) + } else { + _, _ = x509.ParseCertificate(data) + } + return 1 +} + +func FuzzPemDecrypt(data []byte) int { + if len(data) < 6 { + return 0 + } + pemDataLen := int(data[0]) + if (pemDataLen + 5) > len(data) { + return 0 + } + data = data[1:] + pemData := data[:pemDataLen] + password := data[pemDataLen+1:] + block, _ := pem.Decode(pemData) + if block == nil { + return 0 + } + + _, _ = x509.DecryptPEMBlock(block, password) + return 1 +}