fuzzers: fuzzers for bmt, keystore, rlp, trie, whisper (cred to @guidovranken)

This commit is contained in:
Martin Holst Swende 2019-05-19 21:04:17 +02:00
parent f34a3a6805
commit d9535e1146
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
5 changed files with 441 additions and 0 deletions

59
fuzzers/bmt/bmt-fuzzer.go Normal file
View file

@ -0,0 +1,59 @@
package bmt
import (
"bytes"
"fmt"
bmt2 "github.com/ethereum/go-ethereum/swarm/bmt"
"golang.org/x/crypto/sha3"
)
func f1(pool *bmt2.TreePool, input []byte) (int, []byte) {
b := bmt2.New(pool)
b.Reset()
_, err := b.Write(input)
if err != nil {
return 0, nil
}
x := make([]byte, 0, 512)
_, err = b.Write(x)
if err != nil {
return 1, nil
}
return 2, b.Sum(nil)
}
func f2(pool *bmt2.TreePool, input []byte) (int, []byte) {
b := bmt2.New(pool)
b.Reset()
for _, val := range input {
_, err := b.Write([]byte{val})
if err != nil {
return 0, nil
}
}
x := make([]byte, 0, 512)
_, err := b.Write(x)
if err != nil {
return 1, nil
}
return 2, b.Sum(nil)
}
func Fuzz(input []byte) int {
hasher := sha3.NewLegacyKeccak256
pool := bmt2.NewTreePool(hasher, 128, bmt2.PoolSize)
input2 := make([]byte, len(input))
copy(input2, input)
ret1, sum1 := f1(pool, input)
ret2, sum2 := f2(pool, input)
if ret1 != ret2 {
panic(fmt.Sprintf("ret1: %d != ret2: %d", ret1, ret2))
}
if ret1 == 2 && !bytes.Equal(sum1, sum2) {
panic("sums does not match")
}
return 0
}

View file

@ -0,0 +1,21 @@
package keystore
import (
"github.com/ethereum/go-ethereum/accounts/keystore"
"os"
)
func Fuzz(input []byte) int {
ks := keystore.NewKeyStore("/tmp/ks", keystore.LightScryptN, keystore.LightScryptP)
a, err := ks.NewAccount(string(input))
if err != nil {
panic(err)
}
if err := ks.Unlock(a, string(input)); err != nil {
panic(err)
}
os.Remove(a.URL.Path)
return 0
}

113
fuzzers/rlp/rlp_fuzzer.go Normal file
View file

@ -0,0 +1,113 @@
package rlp
import (
"bytes"
"fmt"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
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", i))
}
}
}
func Fuzz(input []byte) int {
var i int
{
if len(input) > 0 {
rlp.Split(input)
}
}
{
if len(input) > 0 {
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++
}
return 0
}

173
fuzzers/trie/trie-fuzzer.go Normal file
View file

@ -0,0 +1,173 @@
package trie
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/trie"
)
// randTest performs random trie operations.
// Instances of this test are created by Generate.
type randTest []randTestStep
type randTestStep struct {
op int
key []byte // for opUpdate, opDelete, opGet
value []byte // for opUpdate
err error // for debugging
}
type proofDb struct{}
func (proofDb) Put(key []byte, value []byte) error {
return nil
}
func (proofDb) Delete(key []byte) error {
return nil
}
const (
opUpdate = iota
opDelete
opGet
opCommit
opHash
opReset
opItercheckhash
opProve
opMax // boundary value, not an actual op
)
type dataSource struct {
input []byte
reader *bytes.Reader
}
func newDataSource(input []byte) *dataSource {
return &dataSource{
input, bytes.NewReader(input),
}
}
func (ds *dataSource) ReadByte() byte {
if b, err := ds.reader.ReadByte(); err != nil {
return 0
} else {
return b
}
}
func (ds *dataSource) Read(buf []byte) (int, error) {
return ds.reader.Read(buf)
}
func (ds *dataSource) Ended() bool {
return ds.reader.Len() == 0
}
func Generate(input []byte) randTest {
var allKeys [][]byte
r := newDataSource(input)
genKey := func() []byte {
if len(allKeys) < 2 || r.ReadByte() < 0x0f {
// new key
key := make([]byte, r.ReadByte()%50)
r.Read(key)
allKeys = append(allKeys, key)
return key
}
// use existing key
return allKeys[int(r.ReadByte())%len(allKeys)]
}
var steps randTest
for i := 0; !r.Ended(); i++ {
step := randTestStep{op: int(r.ReadByte()) % opMax}
switch step.op {
case opUpdate:
step.key = genKey()
step.value = make([]byte, 8)
binary.BigEndian.PutUint64(step.value, uint64(i))
case opGet, opDelete, opProve:
step.key = genKey()
}
steps = append(steps, step)
if len(steps) > 500 {
break
}
}
//fmt.Printf("steps len %d input len %d\n", len(steps), len(input))
return steps
}
func Fuzz(input []byte) int {
program := Generate(input)
if len(program) == 0{
return -1
}
if err := runRandTest(program); err != nil {
panic(err)
}
return 0
}
func runRandTest(rt randTest) error {
triedb := trie.NewDatabase(memorydb.New())
tr, _ := trie.New(common.Hash{}, triedb)
values := make(map[string]string) // tracks content of the trie
for i, step := range rt {
switch step.op {
case opUpdate:
tr.Update(step.key, step.value)
values[string(step.key)] = string(step.value)
case opDelete:
tr.Delete(step.key)
delete(values, string(step.key))
case opGet:
v := tr.Get(step.key)
want := values[string(step.key)]
if string(v) != want {
rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
}
case opCommit:
_, rt[i].err = tr.Commit(nil)
case opHash:
tr.Hash()
case opReset:
hash, err := tr.Commit(nil)
if err != nil {
return err
}
newtr, err := trie.New(hash, triedb)
if err != nil {
return err
}
tr = newtr
case opItercheckhash:
checktr, _ := trie.New(common.Hash{}, triedb)
it := trie.NewIterator(tr.NodeIterator(nil))
for it.Next() {
checktr.Update(it.Key, it.Value)
}
if tr.Hash() != checktr.Hash() {
return fmt.Errorf("hash mismatch in opItercheckhash")
}
case opProve:
rt[i].err = tr.Prove(step.key, 0, proofDb{})
}
// Abort the test on error.
if rt[i].err != nil {
return rt[i].err
}
}
return nil
}

View file

@ -0,0 +1,75 @@
package whisperv6
import (
"bytes"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/whisper/whisperv6"
)
type MessageParams struct {
Topic whisperv6.TopicType
WorkTime uint32
TTL uint32
KeySym []byte
Payload []byte
}
//export fuzzer_entry
func Fuzz(input []byte) int {
var paramsDecoded MessageParams
err := rlp.DecodeBytes(input, &paramsDecoded)
if err != nil {
return 0
}
var params whisperv6.MessageParams
params.KeySym = make([]byte, 32)
if len(paramsDecoded.KeySym) <= 32 {
copy(params.KeySym, paramsDecoded.KeySym)
}
if input[0] == 255 {
params.PoW = 0.01
params.WorkTime = 1
} else {
params.PoW = 0
params.WorkTime = 0
}
params.TTL = paramsDecoded.TTL
params.Payload = paramsDecoded.Payload
text := make([]byte, 0, 512)
text = append(text, params.Payload...)
params.Topic = paramsDecoded.Topic
params.Src, err = crypto.GenerateKey()
if err != nil {
return 0
}
msg, err := whisperv6.NewSentMessage(&params)
if err != nil {
panic(err)
//return
}
env, err := msg.Wrap(&params)
if err != nil {
panic(err)
}
decrypted, err := env.OpenSymmetric(params.KeySym)
if err != nil {
panic(err)
}
if !decrypted.ValidateAndParse() {
panic("ValidateAndParse failed")
return 0
}
if !bytes.Equal(text, decrypted.Payload) {
panic("text != decrypted.Payload")
}
if len(decrypted.Signature) != 65 {
panic("Unexpected signature length")
}
if !whisperv6.IsPubKeyEqual(decrypted.Src, &params.Src.PublicKey) {
panic("Unexpected public key")
}
return 0
}