github.com/core-coin/go-core/v2@v2.1.9/tests/fuzzers/abi/abifuzzer.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package abi
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"math/rand"
    23  	"reflect"
    24  	"strings"
    25  
    26  	fuzz "github.com/google/gofuzz"
    27  
    28  	"github.com/core-coin/go-core/v2/accounts/abi"
    29  	"github.com/core-coin/go-core/v2/crypto"
    30  )
    31  
    32  func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byte) bool {
    33  	outptr := reflect.New(reflect.TypeOf(inputType))
    34  	if err := abi.UnpackIntoInterface(outptr.Interface(), method, input); err == nil {
    35  		output, err := abi.Pack(method, input)
    36  		if err != nil {
    37  			// We have some false positives as we can unpack these type successfully, but not pack them
    38  			if err.Error() == "abi: cannot use []uint8 as type [0]int8 as argument" ||
    39  				err.Error() == "abi: cannot use uint8 as type int8 as argument" {
    40  				return false
    41  			}
    42  			panic(err)
    43  		}
    44  		if !bytes.Equal(input, output[4:]) {
    45  			panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, output[4:]))
    46  		}
    47  		return true
    48  	}
    49  	return false
    50  }
    51  
    52  func packUnpack(abi abi.ABI, method string, input []interface{}) bool {
    53  	if packed, err := abi.Pack(method, input); err == nil {
    54  		outptr := reflect.New(reflect.TypeOf(input))
    55  		err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
    56  		if err != nil {
    57  			panic(err)
    58  		}
    59  		out := outptr.Elem().Interface()
    60  		if !reflect.DeepEqual(input, out) {
    61  			panic(fmt.Sprintf("unpackPack is not equal, \ninput : %x\noutput: %x", input, out))
    62  		}
    63  		return true
    64  	}
    65  	return false
    66  }
    67  
    68  type args struct {
    69  	name string
    70  	typ  string
    71  }
    72  
    73  func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
    74  	sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
    75  	if stateMutability != nil {
    76  		sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
    77  	}
    78  	if payable != nil {
    79  		sig += fmt.Sprintf(`, "payable": %v `, *payable)
    80  	}
    81  	if len(inputs) > 0 {
    82  		sig += `, "inputs" : [ {`
    83  		for i, inp := range inputs {
    84  			sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
    85  			if i+1 < len(inputs) {
    86  				sig += ","
    87  			}
    88  		}
    89  		sig += "} ]"
    90  		sig += `, "outputs" : [ {`
    91  		for i, inp := range inputs {
    92  			sig += fmt.Sprintf(`"name" : "%v", "type" : "%v" `, inp.name, inp.typ)
    93  			if i+1 < len(inputs) {
    94  				sig += ","
    95  			}
    96  		}
    97  		sig += "} ]"
    98  	}
    99  	sig += `}]`
   100  
   101  	return abi.JSON(strings.NewReader(sig))
   102  }
   103  
   104  func fillStruct(structs []interface{}, data []byte) {
   105  	if structs != nil && len(data) != 0 {
   106  		fuzz.NewFromGoFuzz(data).Fuzz(&structs)
   107  	}
   108  }
   109  
   110  func createStructs(args []args) []interface{} {
   111  	structs := make([]interface{}, len(args))
   112  	for i, arg := range args {
   113  		t, err := abi.NewType(arg.typ, "", nil)
   114  		if err != nil {
   115  			panic(err)
   116  		}
   117  		structs[i] = reflect.New(t.GetType()).Elem()
   118  	}
   119  	return structs
   120  }
   121  
   122  func runFuzzer(input []byte) int {
   123  	good := false
   124  
   125  	names := []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
   126  	stateMut := []string{"", "pure", "view", "payable"}
   127  	stateMutabilites := []*string{nil, &stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
   128  	pays := []string{"true", "false"}
   129  	payables := []*string{nil, &pays[0], &pays[1]}
   130  	varNames := []string{"a", "b", "c", "d", "e", "f", "g"}
   131  	varNames = append(varNames, names...)
   132  	varTypes := []string{"bool", "address", "bytes", "string",
   133  		"uint8", "int8", "uint8", "int8", "uint16", "int16",
   134  		"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
   135  		"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
   136  		"uint104", "int104", "uint112", "int112", "uint120", "int120", "uint128", "int128", "uint136", "int136",
   137  		"uint144", "int144", "uint152", "int152", "uint160", "int160", "uint168", "int168", "uint176", "int176",
   138  		"uint184", "int184", "uint192", "int192", "uint200", "int200", "uint208", "int208", "uint216", "int216",
   139  		"uint224", "int224", "uint232", "int232", "uint240", "int240", "uint248", "int248", "uint256", "int256",
   140  		"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11",
   141  		"bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21",
   142  		"bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31",
   143  		"bytes32", "bytes"}
   144  	rnd := rand.New(rand.NewSource(123456))
   145  	if len(input) > 0 {
   146  		kec := crypto.SHA3(input)
   147  		rnd = rand.New(rand.NewSource(int64(kec[0])))
   148  	}
   149  	name := names[rnd.Intn(len(names))]
   150  	stateM := stateMutabilites[rnd.Intn(len(stateMutabilites))]
   151  	payable := payables[rnd.Intn(len(payables))]
   152  	maxLen := 5
   153  	for k := 1; k < maxLen; k++ {
   154  		var arg []args
   155  		for i := k; i > 0; i-- {
   156  			argName := varNames[i]
   157  			argTyp := varTypes[rnd.Int31n(int32(len(varTypes)))]
   158  			if rnd.Int31n(10) == 0 {
   159  				argTyp += "[]"
   160  			} else if rnd.Int31n(10) == 0 {
   161  				arrayArgs := rnd.Int31n(30) + 1
   162  				argTyp += fmt.Sprintf("[%d]", arrayArgs)
   163  			}
   164  			arg = append(arg, args{
   165  				name: argName,
   166  				typ:  argTyp,
   167  			})
   168  		}
   169  		abi, err := createABI(name, stateM, payable, arg)
   170  		if err != nil {
   171  			continue
   172  		}
   173  		structs := createStructs(arg)
   174  		b := unpackPack(abi, name, structs, input)
   175  		fillStruct(structs, input)
   176  		c := packUnpack(abi, name, structs)
   177  		good = good || b || c
   178  	}
   179  	if good {
   180  		return 1
   181  	}
   182  	return 0
   183  }
   184  
   185  func Fuzz(input []byte) int {
   186  	return runFuzzer(input)
   187  }