From 398182284cb1635be833017e87d484795a5e5c56 Mon Sep 17 00:00:00 2001
From: Marius van der Wijden <m.vanderwijden@live.de>
Date: Mon, 18 Jan 2021 14:33:15 +0100
Subject: [PATCH] tests/fuzzers/abi: better test generation (#22158)

* tests/fuzzers/abi: better test generation

* tests/fuzzers/abi: fixed packing issue

* oss-fuzz: enable abi fuzzer
---
 oss-fuzz.sh                         |   6 +-
 tests/fuzzers/abi/abifuzzer.go      | 115 ++++++++++++----------------
 tests/fuzzers/abi/abifuzzer_test.go |   9 +--
 3 files changed, 51 insertions(+), 79 deletions(-)

diff --git a/oss-fuzz.sh b/oss-fuzz.sh
index 5919b2077..7ae4d77b2 100644
--- a/oss-fuzz.sh
+++ b/oss-fuzz.sh
@@ -100,6 +100,7 @@ compile_fuzzer tests/fuzzers/rlp        Fuzz fuzzRlp
 compile_fuzzer tests/fuzzers/trie       Fuzz fuzzTrie
 compile_fuzzer tests/fuzzers/stacktrie  Fuzz fuzzStackTrie
 compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
+compile_fuzzertests/fuzzers/abi         Fuzz fuzzAbi
 
 compile_fuzzer tests/fuzzers/bls12381  FuzzG1Add fuzz_g1_add
 compile_fuzzer tests/fuzzers/bls12381  FuzzG1Mul fuzz_g1_mul
@@ -113,8 +114,3 @@ compile_fuzzer tests/fuzzers/bls12381  FuzzMapG2 fuzz_map_g2
 
 #TODO: move this to tests/fuzzers, if possible
 compile_fuzzer crypto/blake2b  Fuzz      fuzzBlake2b
-
-
-# This doesn't work very well @TODO
-#compile_fuzzertests/fuzzers/abi Fuzz fuzzAbi
-
diff --git a/tests/fuzzers/abi/abifuzzer.go b/tests/fuzzers/abi/abifuzzer.go
index 76d3c800f..8c083b371 100644
--- a/tests/fuzzers/abi/abifuzzer.go
+++ b/tests/fuzzers/abi/abifuzzer.go
@@ -17,38 +17,53 @@
 package abi
 
 import (
-	"bytes"
 	"fmt"
-	"math/rand"
 	"reflect"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/accounts/abi"
-	"github.com/ethereum/go-ethereum/crypto"
 	fuzz "github.com/google/gofuzz"
 )
 
-func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byte) bool {
-	outptr := reflect.New(reflect.TypeOf(inputType))
-	if err := abi.UnpackIntoInterface(outptr.Interface(), method, input); err == nil {
-		output, err := abi.Pack(method, input)
+var (
+	names            = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
+	stateMut         = []string{"", "pure", "view", "payable"}
+	stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
+	pays             = []string{"", "true", "false"}
+	payables         = []*string{&pays[0], &pays[1]}
+	vNames           = []string{"a", "b", "c", "d", "e", "f", "g"}
+	varNames         = append(vNames, names...)
+	varTypes         = []string{"bool", "address", "bytes", "string",
+		"uint8", "int8", "uint8", "int8", "uint16", "int16",
+		"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
+		"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
+		"uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136",
+		"uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176",
+		"uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216",
+		"uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256",
+		"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11",
+		"bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21",
+		"bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31",
+		"bytes32", "bytes"}
+)
+
+func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) {
+	if out, err := abi.Unpack(method, input); err == nil {
+		_, err := abi.Pack(method, out...)
 		if err != nil {
 			// We have some false positives as we can unpack these type successfully, but not pack them
 			if err.Error() == "abi: cannot use []uint8 as type [0]int8 as argument" ||
 				err.Error() == "abi: cannot use uint8 as type int8 as argument" {
-				return false
+				return out, false
 			}
 			panic(err)
 		}
-		if !bytes.Equal(input, output[4:]) {
-			panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, output[4:]))
-		}
-		return true
+		return out, true
 	}
-	return false
+	return nil, false
 }
 
-func packUnpack(abi abi.ABI, method string, input []interface{}) bool {
+func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
 	if packed, err := abi.Pack(method, input); err == nil {
 		outptr := reflect.New(reflect.TypeOf(input))
 		err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
@@ -100,64 +115,23 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab
 	return abi.JSON(strings.NewReader(sig))
 }
 
-func fillStruct(structs []interface{}, data []byte) {
-	if structs != nil && len(data) != 0 {
-		fuzz.NewFromGoFuzz(data).Fuzz(&structs)
-	}
-}
-
-func createStructs(args []args) []interface{} {
-	structs := make([]interface{}, len(args))
-	for i, arg := range args {
-		t, err := abi.NewType(arg.typ, "", nil)
-		if err != nil {
-			panic(err)
-		}
-		structs[i] = reflect.New(t.GetType()).Elem()
-	}
-	return structs
-}
-
 func runFuzzer(input []byte) int {
 	good := false
+	fuzzer := fuzz.NewFromGoFuzz(input)
 
-	names := []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
-	stateMut := []string{"", "pure", "view", "payable"}
-	stateMutabilites := []*string{nil, &stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
-	pays := []string{"true", "false"}
-	payables := []*string{nil, &pays[0], &pays[1]}
-	varNames := []string{"a", "b", "c", "d", "e", "f", "g"}
-	varNames = append(varNames, names...)
-	varTypes := []string{"bool", "address", "bytes", "string",
-		"uint8", "int8", "uint8", "int8", "uint16", "int16",
-		"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
-		"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
-		"uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136",
-		"uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176",
-		"uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216",
-		"uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256",
-		"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11",
-		"bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21",
-		"bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31",
-		"bytes32", "bytes"}
-	rnd := rand.New(rand.NewSource(123456))
-	if len(input) > 0 {
-		kec := crypto.Keccak256(input)
-		rnd = rand.New(rand.NewSource(int64(kec[0])))
-	}
-	name := names[rnd.Intn(len(names))]
-	stateM := stateMutabilites[rnd.Intn(len(stateMutabilites))]
-	payable := payables[rnd.Intn(len(payables))]
+	name := names[getUInt(fuzzer)%len(names)]
+	stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
+	payable := payables[getUInt(fuzzer)%len(payables)]
 	maxLen := 5
 	for k := 1; k < maxLen; k++ {
 		var arg []args
 		for i := k; i > 0; i-- {
 			argName := varNames[i]
-			argTyp := varTypes[rnd.Int31n(int32(len(varTypes)))]
-			if rnd.Int31n(10) == 0 {
+			argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
+			if getUInt(fuzzer)%10 == 0 {
 				argTyp += "[]"
-			} else if rnd.Int31n(10) == 0 {
-				arrayArgs := rnd.Int31n(30) + 1
+			} else if getUInt(fuzzer)%10 == 0 {
+				arrayArgs := getUInt(fuzzer)%30 + 1
 				argTyp += fmt.Sprintf("[%d]", arrayArgs)
 			}
 			arg = append(arg, args{
@@ -169,10 +143,8 @@ func runFuzzer(input []byte) int {
 		if err != nil {
 			continue
 		}
-		structs := createStructs(arg)
-		b := unpackPack(abi, name, structs, input)
-		fillStruct(structs, input)
-		c := packUnpack(abi, name, structs)
+		structs, b := unpackPack(abi, name, input)
+		c := packUnpack(abi, name, &structs)
 		good = good || b || c
 	}
 	if good {
@@ -184,3 +156,12 @@ func runFuzzer(input []byte) int {
 func Fuzz(input []byte) int {
 	return runFuzzer(input)
 }
+
+func getUInt(fuzzer *fuzz.Fuzzer) int {
+	var i int
+	fuzzer.Fuzz(&i)
+	if i < 0 {
+		i *= -1
+	}
+	return i
+}
diff --git a/tests/fuzzers/abi/abifuzzer_test.go b/tests/fuzzers/abi/abifuzzer_test.go
index c72577e9e..c59c45ab1 100644
--- a/tests/fuzzers/abi/abifuzzer_test.go
+++ b/tests/fuzzers/abi/abifuzzer_test.go
@@ -23,13 +23,8 @@ import (
 // TestReplicate can be used to replicate crashers from the fuzzing tests.
 // Just replace testString with the data in .quoted
 func TestReplicate(t *testing.T) {
-	testString := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
-		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00" +
-		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
-		"\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00000000000" +
-		"00000000000000000000" +
-		"00000000000000000000" +
-		"00000001"
+	testString := "N\xef\xbf0\xef\xbf99000000000000" +
+		"000000000000"
 
 	data := []byte(testString)
 	runFuzzer(data)
-- 
GitLab