tests/fuzzers: move fuzzers into native packages (#28467)

This commit is contained in:
Daniel Liu 2024-12-19 11:57:47 +08:00
parent 441f8ed70c
commit 4cc2b2ea5f
8 changed files with 275 additions and 247 deletions

View file

@ -18,6 +18,7 @@ package bitutil
import (
"bytes"
"fmt"
"math/rand"
"testing"
@ -48,19 +49,23 @@ func TestEncodingCycle(t *testing.T) {
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
}
for i, tt := range tests {
data := hexutil.MustDecode(tt)
proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
if err != nil {
t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
continue
}
if !bytes.Equal(data, proc) {
t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data)
if err := testEncodingCycle(hexutil.MustDecode(tt)); err != nil {
t.Errorf("test %d: %v", i, err)
}
}
}
func testEncodingCycle(data []byte) error {
proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
if err != nil {
return fmt.Errorf("failed to decompress compressed data: %v", err)
}
if !bytes.Equal(data, proc) {
return fmt.Errorf("compress/decompress mismatch: have %x, want %x", proc, data)
}
return nil
}
// Tests that data bitset decoding and rencoding works and is bijective.
func TestDecodingCycle(t *testing.T) {
tests := []struct {
@ -179,3 +184,41 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
}
}
func FuzzEncoder(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
if err := testEncodingCycle(data); err != nil {
t.Fatal(err)
}
})
}
func FuzzDecoder(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzDecode(data)
})
}
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
// reencoding algorithm.
func fuzzDecode(data []byte) {
blob, err := DecompressBytes(data, 1024)
if err != nil {
return
}
// re-compress it (it's OK if the re-compressed differs from the
// original - the first input may not have been compressed at all)
comp := CompressBytes(blob)
if len(comp) > len(blob) {
// After compression, it must be smaller or equal
panic("bad compression")
}
// But decompressing it once again should work
decomp, err := DecompressBytes(data, 1024)
if err != nil {
panic(err)
}
if !bytes.Equal(decomp, blob) {
panic("content mismatch")
}
}

View file

@ -0,0 +1,147 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package types
import (
"bytes"
"fmt"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/holiman/uint256"
)
func decodeEncode(input []byte, val interface{}) error {
if err := rlp.DecodeBytes(input, val); err != nil {
// not valid rlp, nothing to do
return nil
}
// If it _were_ valid rlp, we can encode it again
output, err := rlp.EncodeToBytes(val)
if err != nil {
return err
}
if !bytes.Equal(input, output) {
return fmt.Errorf("encode-decode is not equal, \ninput : %x\noutput: %x", input, output)
}
return nil
}
func FuzzRLP(f *testing.F) {
f.Fuzz(fuzzRlp)
}
func fuzzRlp(t *testing.T, input []byte) {
if len(input) == 0 || len(input) > 500*1024 {
return
}
rlp.Split(input)
if elems, _, err := rlp.SplitList(input); err == nil {
rlp.CountValues(elems)
}
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
if err := decodeEncode(input, new(interface{})); err != nil {
t.Fatal(err)
}
{
var v struct {
Int uint
String string
Bytes []byte
}
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
type Types struct {
Bool bool
Raw rlp.RawValue
Slice []*Types
Iface []interface{}
}
var v Types
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
type AllTypes struct {
Int uint
String string
Bytes []byte
Bool bool
Raw rlp.RawValue
Slice []*AllTypes
Array [3]*AllTypes
Iface []interface{}
}
var v AllTypes
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
if err := decodeEncode(input, [10]byte{}); err != nil {
t.Fatal(err)
}
}
{
var v struct {
Byte [10]byte
Rool [10]bool
}
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
{
var h Header
if err := decodeEncode(input, &h); err != nil {
t.Fatal(err)
}
var b Block
if err := decodeEncode(input, &b); err != nil {
t.Fatal(err)
}
var tx Transaction
if err := decodeEncode(input, &tx); err != nil {
t.Fatal(err)
}
var txs Transactions
if err := decodeEncode(input, &txs); err != nil {
t.Fatal(err)
}
var rs Receipts
if err := decodeEncode(input, &rs); err != nil {
t.Fatal(err)
}
}
{
var v struct {
AnIntPtr *big.Int
AnInt big.Int
AnU256Ptr *uint256.Int
AnU256 uint256.Int
NotAnU256 [4]uint64
}
if err := decodeEncode(input, &v); err != nil {
t.Fatal(err)
}
}
}

View file

@ -14,12 +14,31 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package rlp
package vm
import "testing"
import (
"testing"
func Fuzz(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzz(data)
"github.com/XinFinOrg/XDPoSChain/common"
)
func FuzzPrecompiledContracts(f *testing.F) {
// Create list of addresses
var addrs []common.Address
for k := range allPrecompiles {
addrs = append(addrs, k)
}
f.Fuzz(func(t *testing.T, addr uint8, input []byte) {
a := addrs[int(addr)%len(addrs)]
p := allPrecompiles[a]
gas := p.RequiredGas(input)
if gas > 10_000_000 {
return
}
inWant := string(input)
RunPrecompiledContract(nil, p, input, gas)
if inHave := string(input); inWant != inHave {
t.Errorf("Precompiled %v modified input data", a)
}
})
}

View file

@ -18,13 +18,11 @@ package runtime
import (
"testing"
"github.com/XinFinOrg/XDPoSChain/core/vm/runtime"
)
func Fuzz(f *testing.F) {
func FuzzVmRuntime(f *testing.F) {
f.Fuzz(func(t *testing.T, code, input []byte) {
runtime.Execute(code, input, &runtime.Config{
Execute(code, input, &Config{
GasLimit: 12000000,
})
})

View file

@ -1,68 +0,0 @@
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package bitutil
import (
"bytes"
"testing"
"github.com/XinFinOrg/XDPoSChain/common/bitutil"
)
func FuzzEncoder(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzEncode(data)
})
}
func FuzzDecoder(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzDecode(data)
})
}
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
// decoding algorithm.
func fuzzEncode(data []byte) {
proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data))
if !bytes.Equal(data, proc) {
panic("content mismatch")
}
}
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
// reencoding algorithm.
func fuzzDecode(data []byte) {
blob, err := bitutil.DecompressBytes(data, 1024)
if err != nil {
return
}
// re-compress it (it's OK if the re-compressed differs from the
// original - the first input may not have been compressed at all)
comp := bitutil.CompressBytes(blob)
if len(comp) > len(blob) {
// After compression, it must be smaller or equal
panic("bad compression")
}
// But decompressing it once again should work
decomp, err := bitutil.DecompressBytes(data, 1024)
if err != nil {
panic(err)
}
if !bytes.Equal(decomp, blob) {
panic("content mismatch")
}
}

View file

@ -1,143 +0,0 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package rlp
import (
"bytes"
"fmt"
"math/big"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/holiman/uint256"
)
func decodeEncode(input []byte, val interface{}, i int) {
if err := rlp.DecodeBytes(input, val); err == nil {
output, err := rlp.EncodeToBytes(val)
if err != nil {
panic(err)
}
if !bytes.Equal(input, output) {
panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output))
}
}
}
func fuzz(input []byte) int {
if len(input) == 0 {
return 0
}
if len(input) > 500*1024 {
return 0
}
var i int
{
rlp.Split(input)
}
{
if elems, _, err := rlp.SplitList(input); err == nil {
rlp.CountValues(elems)
}
}
{
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
}
{
decodeEncode(input, new(interface{}), i)
i++
}
{
var v struct {
Int uint
String string
Bytes []byte
}
decodeEncode(input, &v, i)
i++
}
{
type Types struct {
Bool bool
Raw rlp.RawValue
Slice []*Types
Iface []interface{}
}
var v Types
decodeEncode(input, &v, i)
i++
}
{
type AllTypes struct {
Int uint
String string
Bytes []byte
Bool bool
Raw rlp.RawValue
Slice []*AllTypes
Array [3]*AllTypes
Iface []interface{}
}
var v AllTypes
decodeEncode(input, &v, i)
i++
}
{
decodeEncode(input, [10]byte{}, i)
i++
}
{
var v struct {
Byte [10]byte
Rool [10]bool
}
decodeEncode(input, &v, i)
i++
}
{
var h types.Header
decodeEncode(input, &h, i)
i++
var b types.Block
decodeEncode(input, &b, i)
i++
var t types.Transaction
decodeEncode(input, &t, i)
i++
var txs types.Transactions
decodeEncode(input, &txs, i)
i++
var rs types.Receipts
decodeEncode(input, &rs, i)
}
{
i++
var v struct {
AnIntPtr *big.Int
AnInt big.Int
AnU256Ptr *uint256.Int
AnU256 uint256.Int
NotAnU256 [4]uint64
}
decodeEncode(input, &v, i)
}
return 1
}

View file

@ -35,7 +35,7 @@ func Fuzz(f *testing.F) {
})
}
func fuzz(dataP1, dataP2 []byte) int {
func fuzz(dataP1, dataP2 []byte) {
var (
curveA = secp256k1.S256()
curveB = btcec.S256()
@ -50,5 +50,4 @@ func fuzz(dataP1, dataP2 []byte) int {
fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2)
panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
}
return 0
}

View file

@ -21,6 +21,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"io"
"math/big"
"math/rand"
"os"
@ -346,8 +347,9 @@ func TestRandomCases(t *testing.T) {
{op: 1, key: common.Hex2Bytes("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24
{op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25
}
runRandTest(rt)
if err := runRandTest(rt); err != nil {
t.Fatal(err)
}
}
// randTest performs random trie operations.
@ -373,36 +375,53 @@ const (
)
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
var finishedFn = func() bool {
size--
return size > 0
}
return reflect.ValueOf(generateSteps(finishedFn, r))
}
func generateSteps(finished func() bool, r io.Reader) randTest {
var allKeys [][]byte
var one = []byte{0}
genKey := func() []byte {
if len(allKeys) < 2 || r.Intn(100) < 10 {
r.Read(one)
if len(allKeys) < 2 || one[0]%100 > 90 {
// new key
key := make([]byte, r.Intn(50))
size := one[0] % 50
key := make([]byte, size)
r.Read(key)
allKeys = append(allKeys, key)
return key
}
// use existing key
return allKeys[r.Intn(len(allKeys))]
idx := int(one[0]) % len(allKeys)
return allKeys[idx]
}
var steps randTest
for i := 0; i < size; i++ {
step := randTestStep{op: r.Intn(opMax)}
for !finished() {
r.Read(one)
step := randTestStep{op: int(one[0]) % opMax}
switch step.op {
case opUpdate:
step.key = genKey()
step.value = make([]byte, 8)
binary.BigEndian.PutUint64(step.value, uint64(i))
binary.BigEndian.PutUint64(step.value, uint64(len(steps)))
case opGet, opDelete:
step.key = genKey()
}
steps = append(steps, step)
}
return reflect.ValueOf(steps)
return steps
}
func runRandTest(rt randTest) bool {
// runRandTestBool coerces error to boolean, for use in quick.Check
func runRandTestBool(rt randTest) bool {
return runRandTest(rt) == nil
}
func runRandTest(rt randTest) error {
triedb := NewDatabase(memorydb.New())
tr, _ := New(common.Hash{}, triedb)
@ -432,12 +451,12 @@ func runRandTest(rt randTest) bool {
hash, err := tr.Commit(nil)
if err != nil {
rt[i].err = err
return false
return err
}
newtr, err := New(hash, triedb)
if err != nil {
rt[i].err = err
return false
return err
}
tr = newtr
case opItercheckhash:
@ -452,14 +471,14 @@ func runRandTest(rt randTest) bool {
}
// Abort the test on error.
if rt[i].err != nil {
return false
return rt[i].err
}
}
return true
return nil
}
func TestRandom(t *testing.T) {
if err := quick.Check(runRandTest, nil); err != nil {
if err := quick.Check(runRandTestBool, nil); err != nil {
if cerr, ok := err.(*quick.CheckError); ok {
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
}
@ -854,3 +873,17 @@ func TestDecodeNode(t *testing.T) {
decodeNode(hash, elems)
}
}
func FuzzTrie(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
var steps = 500
var input = bytes.NewReader(data)
var finishedFn = func() bool {
steps--
return steps < 0 || input.Len() == 0
}
if err := runRandTest(generateSteps(finishedFn, input)); err != nil {
t.Fatal(err)
}
})
}