Skip to content

Commit

Permalink
tests/fuzzers: fuzzbuzz fuzzers for keystore, rlp, trie, whisper (#19910
Browse files Browse the repository at this point in the history
)

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

* fuzzers: move fuzzers to testdata

* testdata/fuzzers: documentation

* testdata/fuzzers: corpus for rlp

* tests/fuzzers: fixup
  • Loading branch information
holiman authored and karalabe committed Dec 10, 2019
1 parent 4b40b53 commit cecc723
Show file tree
Hide file tree
Showing 12 changed files with 529 additions and 0 deletions.
36 changes: 36 additions & 0 deletions fuzzbuzz.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# bmt keystore rlp trie whisperv6

base: ubuntu:16.04
targets:
- name: rlp
language: go
version: "1.13"
corpus: ./fuzzers/rlp/corpus
harness:
function: Fuzz
package: github.com/ethereum/go-ethereum/tests/fuzzers/rlp
checkout: github.com/ethereum/go-ethereum/
- name: keystore
language: go
version: "1.13"
corpus: ./fuzzers/keystore/corpus
harness:
function: Fuzz
package: github.com/ethereum/go-ethereum/tests/fuzzers/keystore
checkout: github.com/ethereum/go-ethereum/
- name: trie
language: go
version: "1.13"
corpus: ./fuzzers/trie/corpus
harness:
function: Fuzz
package: github.com/ethereum/go-ethereum/tests/fuzzers/trie
checkout: github.com/ethereum/go-ethereum/
- name: whisperv6
language: go
version: "1.13"
corpus: ./fuzzers/whisperv6/corpus
harness:
function: Fuzz
package: github.com/ethereum/go-ethereum/tests/fuzzers/whisperv6
checkout: github.com/ethereum/go-ethereum/
45 changes: 45 additions & 0 deletions tests/fuzzers/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## Fuzzers

To run a fuzzer locally, you need [go-fuzz](https://github.com/dvyukov/go-fuzz) installed.

First build a fuzzing-binary out of the selected package:

```
(cd ./rlp && CGO_ENABLED=0 go-fuzz-build .)
```
That command should generate a `rlp-fuzz.zip` in the `rlp/` directory. If you are already in that directory, you can do

```
[user@work rlp]$ go-fuzz
2019/11/26 13:36:54 workers: 6, corpus: 3 (3s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 3s
2019/11/26 13:36:57 workers: 6, corpus: 3 (6s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 1054, uptime: 6s
2019/11/26 13:37:00 workers: 6, corpus: 3 (9s ago), crashers: 0, restarts: 1/8358, execs: 25074 (2786/sec), cover: 1054, uptime: 9s
2019/11/26 13:37:03 workers: 6, corpus: 3 (12s ago), crashers: 0, restarts: 1/8497, execs: 50986 (4249/sec), cover: 1054, uptime: 12s
2019/11/26 13:37:06 workers: 6, corpus: 3 (15s ago), crashers: 0, restarts: 1/9330, execs: 74640 (4976/sec), cover: 1054, uptime: 15s
2019/11/26 13:37:09 workers: 6, corpus: 3 (18s ago), crashers: 0, restarts: 1/9948, execs: 99482 (5527/sec), cover: 1054, uptime: 18s
2019/11/26 13:37:12 workers: 6, corpus: 3 (21s ago), crashers: 0, restarts: 1/9428, execs: 122568 (5836/sec), cover: 1054, uptime: 21s
2019/11/26 13:37:15 workers: 6, corpus: 3 (24s ago), crashers: 0, restarts: 1/9676, execs: 145152 (6048/sec), cover: 1054, uptime: 24s
2019/11/26 13:37:18 workers: 6, corpus: 3 (27s ago), crashers: 0, restarts: 1/9855, execs: 167538 (6205/sec), cover: 1054, uptime: 27s
2019/11/26 13:37:21 workers: 6, corpus: 3 (30s ago), crashers: 0, restarts: 1/9645, execs: 192901 (6430/sec), cover: 1054, uptime: 30s
2019/11/26 13:37:24 workers: 6, corpus: 3 (33s ago), crashers: 0, restarts: 1/9967, execs: 219294 (6645/sec), cover: 1054, uptime: 33s

```
Otherwise:
```
go-fuzz -bin ./rlp/rlp-fuzz.zip
```

### Notes

Once a 'crasher' is found, the fuzzer tries to avoid reporting the same vector twice, so stores the fault in the `suppressions` folder. Thus, if you
e.g. make changes to fix a bug, you should _remove_ all data from the `suppressions`-folder, to verify that the issue is indeed resolved.

Also, if you have only one and the same exit-point for multiple different types of test, the suppression can make the fuzzer hide differnent types of errors. So make
sure that each type of failure is unique (for an example, see the rlp fuzzer, where a counter `i` is used to differentiate between failures:

```golang
if !bytes.Equal(input, output) {
panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output))
}
```

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ns��,��
37 changes: 37 additions & 0 deletions tests/fuzzers/keystore/keystore-fuzzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 keystore

import (
"os"

"github.com/ethereum/go-ethereum/accounts/keystore"
)

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
}
Binary file added tests/fuzzers/rlp/corpus/block_with_uncle.rlp
Binary file not shown.
1 change: 1 addition & 0 deletions tests/fuzzers/rlp/corpus/r.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ˀ����������
2 changes: 2 additions & 0 deletions tests/fuzzers/rlp/corpus/transaction.rlp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
�N�������
���a����P?-'�{�ЋD�Y���f�j\�E��~읕��F?1(�ij6�@�v �L��ڑ�
127 changes: 127 additions & 0 deletions tests/fuzzers/rlp/rlp_fuzzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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"

"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, \ninput : %x\noutput: %x", i, input, output))
}
}
}

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)
}
return 0
}
1 change: 1 addition & 0 deletions tests/fuzzers/trie/corpus/data
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
asdlfkjasf23oiejfasdfadkfqlkjfasdlkfjalwk4jfalsdkfjawlefkjsadlfkjasldkfjwalefkjasdlfkjM
Loading

0 comments on commit cecc723

Please sign in to comment.