mirror of
https://github.com/tendermint/tendermint.git
synced 2025-12-23 06:15:19 +00:00
test/fuzz: move fuzz tests into this repo (#5918)
Co-authored-by: Emmanuel T Odeke <emmanuel@orijtech.com> Closes #5907 - add init-corpus to blockchain reactor - remove validator-set FromBytes test now that we have proto, we don't need to test it! bye amino - simplify mempool test do we want to test remote ABCI app? - do not recreate mux on every crash in jsonrpc test - update p2p pex reactor test - remove p2p/listener test the API has changed + I did not understand what it's tested anyway - update secretconnection test - add readme and makefile - list inputs in readme - add nightly workflow - remove blockchain fuzz test EncodeMsg / DecodeMsg no longer exist
This commit is contained in:
63
.github/workflows/fuzz-nightly.yml
vendored
Normal file
63
.github/workflows/fuzz-nightly.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# Runs fuzzing nightly.
|
||||
name: fuzz-nightly
|
||||
on:
|
||||
workflow_dispatch: # allow running workflow manually
|
||||
schedule:
|
||||
- cron: '0 3 * * *'
|
||||
|
||||
jobs:
|
||||
fuzz-nightly-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.15'
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install go-fuzz
|
||||
working-directory: test/fuzz
|
||||
run: go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
- name: Fuzz mempool
|
||||
working-directory: test/fuzz
|
||||
run: timeout 10m make fuzz-mempool
|
||||
|
||||
- name: Fuzz p2p-addrbook
|
||||
working-directory: test/fuzz
|
||||
run: timeout 10m make fuzz-p2p-addrbook
|
||||
|
||||
- name: Fuzz p2p-pex
|
||||
working-directory: test/fuzz
|
||||
run: timeout 10m make fuzz-p2p-pex
|
||||
|
||||
- name: Fuzz p2p-sc
|
||||
working-directory: test/fuzz
|
||||
run: timeout 10m make fuzz-p2p-sc
|
||||
|
||||
- name: Fuzz p2p-rpc-server
|
||||
working-directory: test/fuzz
|
||||
run: timeout 10m make fuzz-rpc-server
|
||||
|
||||
- name: Set crashers count
|
||||
working-directory: test/fuzz
|
||||
run: echo "::set-output name=crashers-count::$(find . -type d -name "crashers" | xargs -I % sh -c 'ls % | wc -l' | awk '{total += $1} END {print total}')"
|
||||
id: set-crashers-count
|
||||
outputs:
|
||||
crashers_count: ${{ steps.set-crashers-count.outputs.crashers-count }}
|
||||
|
||||
fuzz-nightly-fail:
|
||||
needs: fuzz-nightly-test
|
||||
if: ${{ needs.set-crashers-count.outputs.crashers-count != 0 }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Notify Slack if any crashers
|
||||
uses: rtCamp/action-slack-notify@ae4223259071871559b6e9d08b24a63d71b3f0c0
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_CHANNEL: tendermint-internal
|
||||
SLACK_USERNAME: Nightly Fuzz Tests
|
||||
SLACK_ICON_EMOJI: ':skull:'
|
||||
SLACK_COLOR: danger
|
||||
SLACK_MESSAGE: Crashers found in Nightly Fuzz tests
|
||||
SLACK_FOOTER: ''
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -45,5 +45,14 @@ addrbook.json
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
terraform.tfstate.d
|
||||
|
||||
profile\.out
|
||||
test/e2e/build
|
||||
test/e2e/networks/*/
|
||||
test/logs
|
||||
test/maverick/maverick
|
||||
test/p2p/data/
|
||||
vendor
|
||||
test/fuzz/**/corpus
|
||||
test/fuzz/**/crashers
|
||||
test/fuzz/**/suppressions
|
||||
test/fuzz/**/*.zip
|
||||
|
||||
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
||||
github.com/btcsuite/btcd v0.21.0-beta
|
||||
github.com/btcsuite/btcutil v1.0.2
|
||||
github.com/confio/ics23/go v0.6.3
|
||||
github.com/cosmos/iavl v0.15.0-rc5
|
||||
github.com/cosmos/iavl v0.15.3
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/go-kit/kit v0.10.0
|
||||
github.com/go-logfmt/logfmt v0.5.0
|
||||
|
||||
7
go.sum
7
go.sum
@@ -102,6 +102,8 @@ github.com/cosmos/iavl v0.15.0-rc3.0.20201009144442-230e9bdf52cd h1:K3bmPkMDnd2K
|
||||
github.com/cosmos/iavl v0.15.0-rc3.0.20201009144442-230e9bdf52cd/go.mod h1:3xOIaNNX19p0QrX0VqWa6voPRoJRGGYtny+DH8NEPvE=
|
||||
github.com/cosmos/iavl v0.15.0-rc5 h1:AMKgaAjXwGANWv56NL4q4hV+a0puSkLYD6cCQAv3i44=
|
||||
github.com/cosmos/iavl v0.15.0-rc5/go.mod h1:WqoPL9yPTQ85QBMT45OOUzPxG/U/JcJoN7uMjgxke/I=
|
||||
github.com/cosmos/iavl v0.15.3 h1:xE9r6HW8GeKeoYJN4zefpljZ1oukVScP/7M8oj6SUts=
|
||||
github.com/cosmos/iavl v0.15.3/go.mod h1:OLjQiAQ4fGD2KDZooyJG9yz+p2ao2IAYSbke8mVvSA4=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
@@ -531,6 +533,7 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4=
|
||||
github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg=
|
||||
github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ=
|
||||
github.com/tendermint/tm-db v0.6.2 h1:DOn8jwCdjJblrCFJbtonEIPD1IuJWpbRUUdR8GWE4RM=
|
||||
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
|
||||
github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg=
|
||||
@@ -609,6 +612,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -728,6 +732,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -762,6 +767,8 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhK
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6 h1:iRN4+t0lvZX/l9gH14ARF9i58tsVa5a97k6aH95rC3Y=
|
||||
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 h1:Rt0FRalMgdSlXAVJvX4pr65KfqaxHXSLkSJRD9pw6g0=
|
||||
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
|
||||
@@ -14,3 +14,9 @@ and run the following tests in docker containers:
|
||||
- counter app over grpc
|
||||
- persistence tests
|
||||
- crash tendermint at each of many predefined points, restart, and ensure it syncs properly with the app
|
||||
|
||||
## Fuzzing
|
||||
|
||||
[Fuzzing](https://en.wikipedia.org/wiki/Fuzzing) of various system inputs.
|
||||
|
||||
See `./fuzz/README.md` for more details.
|
||||
|
||||
39
test/fuzz/Makefile
Normal file
39
test/fuzz/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
.PHONY: fuzz-mempool
|
||||
fuzz-mempool:
|
||||
cd mempool && \
|
||||
rm -f *-fuzz.zip && \
|
||||
go-fuzz-build && \
|
||||
go-fuzz
|
||||
|
||||
.PHONY: fuzz-p2p-addrbook
|
||||
fuzz-p2p-addrbook:
|
||||
cd p2p/addrbook && \
|
||||
rm -f *-fuzz.zip && \
|
||||
go run ./init-corpus/main.go && \
|
||||
go-fuzz-build && \
|
||||
go-fuzz
|
||||
|
||||
.PHONY: fuzz-p2p-pex
|
||||
fuzz-p2p-pex:
|
||||
cd p2p/pex && \
|
||||
rm -f *-fuzz.zip && \
|
||||
go run ./init-corpus/main.go && \
|
||||
go-fuzz-build && \
|
||||
go-fuzz
|
||||
|
||||
.PHONY: fuzz-p2p-sc
|
||||
fuzz-p2p-sc:
|
||||
cd p2p/secret_connection && \
|
||||
rm -f *-fuzz.zip && \
|
||||
go run ./init-corpus/main.go && \
|
||||
go-fuzz-build && \
|
||||
go-fuzz
|
||||
|
||||
.PHONY: fuzz-rpc-server
|
||||
fuzz-rpc-server:
|
||||
cd rpc/jsonrpc/server && \
|
||||
rm -f *-fuzz.zip && \
|
||||
go-fuzz-build && \
|
||||
go-fuzz
|
||||
72
test/fuzz/README.md
Normal file
72
test/fuzz/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# fuzz
|
||||
|
||||
Fuzzing for various packages in Tendermint using [go-fuzz](https://github.com/dvyukov/go-fuzz) library.
|
||||
|
||||
Inputs:
|
||||
|
||||
- mempool `CheckTx` (using kvstore in-process ABCI app)
|
||||
- p2p `Addrbook#AddAddress`
|
||||
- p2p `pex.Reactor#Receive`
|
||||
- p2p `SecretConnection#Read` and `SecretConnection#Write`
|
||||
- rpc jsonrpc server
|
||||
|
||||
## Directory structure
|
||||
|
||||
```
|
||||
| test
|
||||
| |- corpus/
|
||||
| |- crashers/
|
||||
| |- init-corpus/
|
||||
| |- suppressions/
|
||||
| |- testdata/
|
||||
| |- <testname>.go
|
||||
```
|
||||
|
||||
`/corpus` directory contains corpus data. The idea is to help the fuzzier to
|
||||
understand what bytes sequences are semantically valid (e.g. if we're testing
|
||||
PNG decoder, then we would put black-white PNG into corpus directory; with
|
||||
blockchain reactor - we would put blockchain messages into corpus).
|
||||
|
||||
`/init-corpus` (if present) contains a script for generating corpus data.
|
||||
|
||||
`/testdata` directory may contain an additional data (like `addrbook.json`).
|
||||
|
||||
Upon running the fuzzier, `/crashers` and `/suppressions` dirs will be created,
|
||||
along with <testname>.zip archive. `/crashers` will show any inputs, which have
|
||||
lead to panics (plus a trace). `/suppressions` will show any suppressed inputs.
|
||||
|
||||
## Running
|
||||
|
||||
```sh
|
||||
make fuzz-mempool
|
||||
make fuzz-p2p-addrbook
|
||||
make fuzz-p2p-pex
|
||||
make fuzz-p2p-sc
|
||||
make fuzz-rpc-server
|
||||
```
|
||||
|
||||
Each command will create corpus data (if needed), generate a fuzz archive and
|
||||
call `go-fuzz` executable.
|
||||
|
||||
Then watch out for the respective outputs in the fuzzer output to announce new
|
||||
crashers which can be found in the directory `crashers`.
|
||||
|
||||
For example if we find
|
||||
|
||||
```sh
|
||||
ls crashers/
|
||||
61bde465f47c93254d64d643c3b2480e0a54666e
|
||||
61bde465f47c93254d64d643c3b2480e0a54666e.output
|
||||
61bde465f47c93254d64d643c3b2480e0a54666e.quoted
|
||||
da39a3ee5e6b4b0d3255bfef95601890afd80709
|
||||
da39a3ee5e6b4b0d3255bfef95601890afd80709.output
|
||||
da39a3ee5e6b4b0d3255bfef95601890afd80709.quoted
|
||||
```
|
||||
|
||||
the crashing bytes generated by the fuzzer will be in
|
||||
`61bde465f47c93254d64d643c3b2480e0a54666e` the respective crash report in
|
||||
`61bde465f47c93254d64d643c3b2480e0a54666e.output`
|
||||
|
||||
and the bug report can be created by retrieving the bytes in
|
||||
`61bde465f47c93254d64d643c3b2480e0a54666e` and feeding those back into the
|
||||
`Fuzz` function.
|
||||
34
test/fuzz/mempool/checktx.go
Normal file
34
test/fuzz/mempool/checktx.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package checktx
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
"github.com/tendermint/tendermint/config"
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
)
|
||||
|
||||
var mempool mempl.Mempool
|
||||
|
||||
func init() {
|
||||
app := kvstore.NewApplication()
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
appConnMem, _ := cc.NewABCIClient()
|
||||
err := appConnMem.Start()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg := config.DefaultMempoolConfig()
|
||||
cfg.Broadcast = false
|
||||
|
||||
mempool = mempl.NewCListMempool(cfg, appConnMem, 0)
|
||||
}
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
err := mempool.CheckTx(data, nil, mempl.TxInfo{})
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
35
test/fuzz/p2p/addrbook/fuzz.go
Normal file
35
test/fuzz/p2p/addrbook/fuzz.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// nolint: gosec
|
||||
package addr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/p2p/pex"
|
||||
)
|
||||
|
||||
var addrBook = pex.NewAddrBook("./testdata/addrbook.json", true)
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
addr := new(p2p.NetAddress)
|
||||
if err := json.Unmarshal(data, addr); err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Fuzz AddAddress.
|
||||
err := addrBook.AddAddress(addr, addr)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Also, make sure PickAddress always returns a non-nil address.
|
||||
bias := rand.Intn(100)
|
||||
if p := addrBook.PickAddress(bias); p == nil {
|
||||
panic(fmt.Sprintf("picked a nil address (bias: %d, addrBook size: %v)",
|
||||
bias, addrBook.Size()))
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
58
test/fuzz/p2p/addrbook/init-corpus/main.go
Normal file
58
test/fuzz/p2p/addrbook/init-corpus/main.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// nolint: gosec
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baseDir := flag.String("base", ".", `where the "corpus" directory will live`)
|
||||
flag.Parse()
|
||||
|
||||
initCorpus(*baseDir)
|
||||
}
|
||||
|
||||
func initCorpus(baseDir string) {
|
||||
log.SetFlags(0)
|
||||
|
||||
// create "corpus" directory
|
||||
corpusDir := filepath.Join(baseDir, "corpus")
|
||||
if err := os.MkdirAll(corpusDir, 0755); err != nil {
|
||||
log.Fatalf("Creating %q err: %v", corpusDir, err)
|
||||
}
|
||||
|
||||
// create corpus
|
||||
privKey := ed25519.GenPrivKey()
|
||||
addrs := []*p2p.NetAddress{
|
||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(0, 0, 0, 0), Port: 0},
|
||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(127, 0, 0, 0), Port: 80},
|
||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(213, 87, 10, 200), Port: 8808},
|
||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(111, 111, 111, 111), Port: 26656},
|
||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.ParseIP("2001:db8::68"), Port: 26656},
|
||||
}
|
||||
|
||||
for i, addr := range addrs {
|
||||
filename := filepath.Join(corpusDir, fmt.Sprintf("%d.json", i))
|
||||
|
||||
bz, err := json.Marshal(addr)
|
||||
if err != nil {
|
||||
log.Fatalf("can't marshal %v: %v", addr, err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filename, bz, 0644); err != nil {
|
||||
log.Fatalf("can't write %v to %q: %v", addr, filename, err)
|
||||
}
|
||||
|
||||
log.Printf("wrote %q", filename)
|
||||
}
|
||||
}
|
||||
82
test/fuzz/p2p/pex/init-corpus/main.go
Normal file
82
test/fuzz/p2p/pex/init-corpus/main.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// nolint: gosec
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baseDir := flag.String("base", ".", `where the "corpus" directory will live`)
|
||||
flag.Parse()
|
||||
|
||||
initCorpus(*baseDir)
|
||||
}
|
||||
|
||||
func initCorpus(rootDir string) {
|
||||
log.SetFlags(0)
|
||||
|
||||
corpusDir := filepath.Join(rootDir, "corpus")
|
||||
if err := os.MkdirAll(corpusDir, 0755); err != nil {
|
||||
log.Fatalf("Creating %q err: %v", corpusDir, err)
|
||||
}
|
||||
sizes := []int{0, 1, 2, 17, 5, 31}
|
||||
|
||||
// Make the PRNG predictable
|
||||
rand.Seed(10)
|
||||
|
||||
for _, n := range sizes {
|
||||
var addrs []*p2p.NetAddress
|
||||
|
||||
// IPv4 addresses
|
||||
for i := 0; i < n; i++ {
|
||||
privKey := ed25519.GenPrivKey()
|
||||
addr := fmt.Sprintf(
|
||||
"%s@%v.%v.%v.%v:26656",
|
||||
p2p.NodeIDFromPubKey(privKey.PubKey()),
|
||||
rand.Int()%256,
|
||||
rand.Int()%256,
|
||||
rand.Int()%256,
|
||||
rand.Int()%256,
|
||||
)
|
||||
netAddr, _ := p2p.NewNetAddressString(addr)
|
||||
addrs = append(addrs, netAddr)
|
||||
}
|
||||
|
||||
// IPv6 addresses
|
||||
privKey := ed25519.GenPrivKey()
|
||||
ipv6a, err := p2p.NewNetAddressString(
|
||||
fmt.Sprintf("%s@[ff02::1:114]:26656", p2p.NodeIDFromPubKey(privKey.PubKey())))
|
||||
if err != nil {
|
||||
log.Fatalf("can't create a new netaddress: %v", err)
|
||||
}
|
||||
addrs = append(addrs, ipv6a)
|
||||
|
||||
msg := tmp2p.Message{
|
||||
Sum: &tmp2p.Message_PexAddrs{
|
||||
PexAddrs: &tmp2p.PexAddrs{Addrs: p2p.NetAddressesToProto(addrs)},
|
||||
},
|
||||
}
|
||||
bz, err := msg.Marshal()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to marshal: %v", err)
|
||||
}
|
||||
|
||||
filename := filepath.Join(rootDir, "corpus", fmt.Sprintf("%d", n))
|
||||
|
||||
if err := ioutil.WriteFile(filename, bz, 0644); err != nil {
|
||||
log.Fatalf("can't write %X to %q: %v", bz, filename, err)
|
||||
}
|
||||
|
||||
log.Printf("wrote %q", filename)
|
||||
}
|
||||
}
|
||||
86
test/fuzz/p2p/pex/reactor_receive.go
Normal file
86
test/fuzz/p2p/pex/reactor_receive.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package pex
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/p2p/pex"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
var (
|
||||
pexR *pex.Reactor
|
||||
peer p2p.Peer
|
||||
)
|
||||
|
||||
func init() {
|
||||
addrB := pex.NewAddrBook("./testdata/addrbook1", false)
|
||||
pexR := pex.NewReactor(addrB, &pex.ReactorConfig{SeedMode: false})
|
||||
if pexR == nil {
|
||||
panic("NewReactor returned nil")
|
||||
}
|
||||
pexR.SetLogger(log.NewNopLogger())
|
||||
peer := newFuzzPeer()
|
||||
pexR.AddPeer(peer)
|
||||
|
||||
}
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
// MakeSwitch uses log.TestingLogger which can't be executed in init()
|
||||
cfg := config.DefaultP2PConfig()
|
||||
cfg.PexReactor = true
|
||||
sw := p2p.MakeSwitch(cfg, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
|
||||
return sw
|
||||
})
|
||||
pexR.SetSwitch(sw)
|
||||
|
||||
pexR.Receive(pex.PexChannel, peer, data)
|
||||
return 1
|
||||
}
|
||||
|
||||
type fuzzPeer struct {
|
||||
*service.BaseService
|
||||
m map[string]interface{}
|
||||
}
|
||||
|
||||
var _ p2p.Peer = (*fuzzPeer)(nil)
|
||||
|
||||
func newFuzzPeer() *fuzzPeer {
|
||||
fp := &fuzzPeer{m: make(map[string]interface{})}
|
||||
fp.BaseService = service.NewBaseService(nil, "fuzzPeer", fp)
|
||||
return fp
|
||||
}
|
||||
|
||||
var privKey = ed25519.GenPrivKey()
|
||||
var nodeID = p2p.NodeIDFromPubKey(privKey.PubKey())
|
||||
var defaultNodeInfo = p2p.NodeInfo{
|
||||
ProtocolVersion: p2p.NewProtocolVersion(
|
||||
version.P2PProtocol,
|
||||
version.BlockProtocol,
|
||||
0,
|
||||
),
|
||||
NodeID: nodeID,
|
||||
ListenAddr: "0.0.0.0:98992",
|
||||
Moniker: "foo1",
|
||||
}
|
||||
|
||||
func (fp *fuzzPeer) FlushStop() {}
|
||||
func (fp *fuzzPeer) ID() p2p.NodeID { return nodeID }
|
||||
func (fp *fuzzPeer) RemoteIP() net.IP { return net.IPv4(0, 0, 0, 0) }
|
||||
func (fp *fuzzPeer) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: fp.RemoteIP(), Port: 98991, Zone: ""}
|
||||
}
|
||||
func (fp *fuzzPeer) IsOutbound() bool { return false }
|
||||
func (fp *fuzzPeer) IsPersistent() bool { return false }
|
||||
func (fp *fuzzPeer) CloseConn() error { return nil }
|
||||
func (fp *fuzzPeer) NodeInfo() p2p.NodeInfo { return defaultNodeInfo }
|
||||
func (fp *fuzzPeer) Status() p2p.ConnectionStatus { var cs p2p.ConnectionStatus; return cs }
|
||||
func (fp *fuzzPeer) SocketAddr() *p2p.NetAddress { return p2p.NewNetAddress(fp.ID(), fp.RemoteAddr()) }
|
||||
func (fp *fuzzPeer) Send(byte, []byte) bool { return true }
|
||||
func (fp *fuzzPeer) TrySend(byte, []byte) bool { return true }
|
||||
func (fp *fuzzPeer) Set(key string, value interface{}) { fp.m[key] = value }
|
||||
func (fp *fuzzPeer) Get(key string) interface{} { return fp.m[key] }
|
||||
1705
test/fuzz/p2p/pex/testdata/addrbook1
vendored
Normal file
1705
test/fuzz/p2p/pex/testdata/addrbook1
vendored
Normal file
File diff suppressed because it is too large
Load Diff
48
test/fuzz/p2p/secret_connection/init-corpus/main.go
Normal file
48
test/fuzz/p2p/secret_connection/init-corpus/main.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// nolint: gosec
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baseDir := flag.String("base", ".", `where the "corpus" directory will live`)
|
||||
flag.Parse()
|
||||
|
||||
initCorpus(*baseDir)
|
||||
}
|
||||
|
||||
func initCorpus(baseDir string) {
|
||||
log.SetFlags(0)
|
||||
|
||||
corpusDir := filepath.Join(baseDir, "corpus")
|
||||
if err := os.MkdirAll(corpusDir, 0755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
data := []string{
|
||||
"dadc04c2-cfb1-4aa9-a92a-c0bf780ec8b6",
|
||||
"",
|
||||
" ",
|
||||
" a ",
|
||||
`{"a": 12, "tsp": 999, k: "blue"}`,
|
||||
`9999.999`,
|
||||
`""`,
|
||||
`Tendermint fuzzing`,
|
||||
}
|
||||
|
||||
for i, datum := range data {
|
||||
filename := filepath.Join(corpusDir, fmt.Sprintf("%d", i))
|
||||
|
||||
if err := ioutil.WriteFile(filename, []byte(datum), 0644); err != nil {
|
||||
log.Fatalf("can't write %v to %q: %v", datum, filename, err)
|
||||
}
|
||||
|
||||
log.Printf("wrote %q", filename)
|
||||
}
|
||||
}
|
||||
107
test/fuzz/p2p/secret_connection/read_write.go
Normal file
107
test/fuzz/p2p/secret_connection/read_write.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package secretconnection
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/libs/async"
|
||||
sc "github.com/tendermint/tendermint/p2p/conn"
|
||||
)
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
if len(data) == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
fooConn, barConn := makeSecretConnPair()
|
||||
n, err := fooConn.Write(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dataRead := make([]byte, n)
|
||||
m, err := barConn.Read(dataRead)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !bytes.Equal(data[:n], dataRead[:m]) {
|
||||
panic(fmt.Sprintf("bytes written %X != read %X", data[:n], dataRead[:m]))
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
type kvstoreConn struct {
|
||||
*io.PipeReader
|
||||
*io.PipeWriter
|
||||
}
|
||||
|
||||
func (drw kvstoreConn) Close() (err error) {
|
||||
err2 := drw.PipeWriter.CloseWithError(io.EOF)
|
||||
err1 := drw.PipeReader.Close()
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
return err1
|
||||
}
|
||||
|
||||
// Each returned ReadWriteCloser is akin to a net.Connection
|
||||
func makeKVStoreConnPair() (fooConn, barConn kvstoreConn) {
|
||||
barReader, fooWriter := io.Pipe()
|
||||
fooReader, barWriter := io.Pipe()
|
||||
return kvstoreConn{fooReader, fooWriter}, kvstoreConn{barReader, barWriter}
|
||||
}
|
||||
|
||||
func makeSecretConnPair() (fooSecConn, barSecConn *sc.SecretConnection) {
|
||||
var (
|
||||
fooConn, barConn = makeKVStoreConnPair()
|
||||
fooPrvKey = ed25519.GenPrivKey()
|
||||
fooPubKey = fooPrvKey.PubKey()
|
||||
barPrvKey = ed25519.GenPrivKey()
|
||||
barPubKey = barPrvKey.PubKey()
|
||||
)
|
||||
|
||||
// Make connections from both sides in parallel.
|
||||
var trs, ok = async.Parallel(
|
||||
func(_ int) (val interface{}, abort bool, err error) {
|
||||
fooSecConn, err = sc.MakeSecretConnection(fooConn, fooPrvKey)
|
||||
if err != nil {
|
||||
log.Printf("failed to establish SecretConnection for foo: %v", err)
|
||||
return nil, true, err
|
||||
}
|
||||
remotePubBytes := fooSecConn.RemotePubKey()
|
||||
if !remotePubBytes.Equals(barPubKey) {
|
||||
err = fmt.Errorf("unexpected fooSecConn.RemotePubKey. Expected %v, got %v",
|
||||
barPubKey, fooSecConn.RemotePubKey())
|
||||
log.Print(err)
|
||||
return nil, true, err
|
||||
}
|
||||
return nil, false, nil
|
||||
},
|
||||
func(_ int) (val interface{}, abort bool, err error) {
|
||||
barSecConn, err = sc.MakeSecretConnection(barConn, barPrvKey)
|
||||
if barSecConn == nil {
|
||||
log.Printf("failed to establish SecretConnection for bar: %v", err)
|
||||
return nil, true, err
|
||||
}
|
||||
remotePubBytes := barSecConn.RemotePubKey()
|
||||
if !remotePubBytes.Equals(fooPubKey) {
|
||||
err = fmt.Errorf("unexpected barSecConn.RemotePubKey. Expected %v, got %v",
|
||||
fooPubKey, barSecConn.RemotePubKey())
|
||||
log.Print(err)
|
||||
return nil, true, err
|
||||
}
|
||||
return nil, false, nil
|
||||
},
|
||||
)
|
||||
|
||||
if trs.FirstError() != nil {
|
||||
log.Fatalf("unexpected error: %v", trs.FirstError())
|
||||
}
|
||||
if !ok {
|
||||
log.Fatal("Unexpected task abortion")
|
||||
}
|
||||
|
||||
return fooSecConn, barSecConn
|
||||
}
|
||||
44
test/fuzz/rpc/jsonrpc/server/handler.go
Normal file
44
test/fuzz/rpc/jsonrpc/server/handler.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
rs "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
)
|
||||
|
||||
var rpcFuncMap = map[string]*rs.RPCFunc{
|
||||
"c": rs.NewRPCFunc(func(s string, i int) (string, int) { return "foo", 200 }, "s,i"),
|
||||
}
|
||||
var mux *http.ServeMux
|
||||
|
||||
func init() {
|
||||
mux := http.NewServeMux()
|
||||
buf := new(bytes.Buffer)
|
||||
lgr := log.NewTMLogger(buf)
|
||||
rs.RegisterRPCFuncs(mux, rpcFuncMap, lgr)
|
||||
}
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
req, _ := http.NewRequest("POST", "http://localhost/", bytes.NewReader(data))
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
res := rec.Result()
|
||||
blob, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := res.Body.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recv := new(types.RPCResponse)
|
||||
if err := json.Unmarshal(blob, recv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
Reference in New Issue
Block a user