github.com/jimmyx0x/go-ethereum@v1.10.28/tests/fuzzers/modexp/modexp-fuzzer.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package modexp
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/core/vm"
    25  	big2 "github.com/holiman/big"
    26  )
    27  
    28  // Fuzz is the fuzzing entry-point.
    29  // The function must return
    30  //
    31  //   - 1 if the fuzzer should increase priority of the
    32  //     given input during subsequent fuzzing (for example, the input is lexically
    33  //     correct and was parsed successfully);
    34  //   - -1 if the input must not be added to corpus even if gives new coverage; and
    35  //   - 0 otherwise
    36  //
    37  // other values are reserved for future use.
    38  func Fuzz(input []byte) int {
    39  	if len(input) <= 96 {
    40  		return -1
    41  	}
    42  	// Abort on too expensive inputs
    43  	precomp := vm.PrecompiledContractsBerlin[common.BytesToAddress([]byte{5})]
    44  	if gas := precomp.RequiredGas(input); gas > 40_000_000 {
    45  		return 0
    46  	}
    47  	var (
    48  		baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
    49  		expLen  = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
    50  		modLen  = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64()
    51  	)
    52  	// Handle a special case when both the base and mod length is zero
    53  	if baseLen == 0 && modLen == 0 {
    54  		return -1
    55  	}
    56  	input = input[96:]
    57  	// Retrieve the operands and execute the exponentiation
    58  	var (
    59  		base  = new(big.Int).SetBytes(getData(input, 0, baseLen))
    60  		exp   = new(big.Int).SetBytes(getData(input, baseLen, expLen))
    61  		mod   = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
    62  		base2 = new(big2.Int).SetBytes(getData(input, 0, baseLen))
    63  		exp2  = new(big2.Int).SetBytes(getData(input, baseLen, expLen))
    64  		mod2  = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen))
    65  	)
    66  	if mod.BitLen() == 0 {
    67  		// Modulo 0 is undefined, return zero
    68  		return -1
    69  	}
    70  	var a = new(big2.Int).Exp(base2, exp2, mod2).String()
    71  	var b = new(big.Int).Exp(base, exp, mod).String()
    72  	if a != b {
    73  		panic(fmt.Sprintf("Inequality %#x ^ %#x mod %#x \n have %s\n want %s", base, exp, mod, a, b))
    74  	}
    75  	return 1
    76  }
    77  
    78  // getData returns a slice from the data based on the start and size and pads
    79  // up to size with zero's. This function is overflow safe.
    80  func getData(data []byte, start uint64, size uint64) []byte {
    81  	length := uint64(len(data))
    82  	if start > length {
    83  		start = length
    84  	}
    85  	end := start + size
    86  	if end > length {
    87  		end = length
    88  	}
    89  	return common.RightPadBytes(data[start:end], int(size))
    90  }