Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Go test output
*/*.test
*/*.out
fuzzing/

# Build output
build
Expand Down
34 changes: 34 additions & 0 deletions FUZZING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Fuzzing

Fuzzing is mainly applicable to packages that parse complex inputs (both text and binary), and is especially useful for hardening of systems that parse inputs from potentially malicious users (e.g. anything accepted over a network).
To run a p2p protocol fuzzer locally, use the command:

```
make fuzz
```
That command should generate a `gossip-fuzz.zip` in the `fuzzing/` directory and runs fuzzer:

```
2020/12/30 22:50:51 workers: 0, corpus: 1 (3s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 3s
2020/12/30 22:50:54 workers: 0, corpus: 1 (6s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 6s
2020/12/30 22:50:57 workers: 3, corpus: 1 (9s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 9s
2020/12/30 22:51:00 workers: 3, corpus: 1 (12s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 12s
2020/12/30 22:51:03 workers: 3, corpus: 1 (15s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 15s
2020/12/30 22:51:06 workers: 3, corpus: 1 (18s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 18s
2020/12/30 22:51:09 workers: 3, corpus: 1 (21s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 21s
2020/12/30 22:51:12 workers: 3, corpus: 1 (24s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 24s
2020/12/30 22:51:15 workers: 3, corpus: 1 (27s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 27s
2020/12/30 22:51:18 workers: 3, corpus: 1 (30s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 30s
2020/12/30 22:51:21 workers: 3, corpus: 1 (33s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 33s
2020/12/30 22:51:24 workers: 3, corpus: 1 (36s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 36s
2020/12/30 22:51:27 workers: 3, corpus: 1 (39s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 39s
2020/12/30 22:51:30 workers: 3, corpus: 1 (42s ago), crashers: 1, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 42s
```

See [github.com/dvyukov/go-fuzz](https://github.com/dvyukov/go-fuzz) to get more details.


### 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.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ coverage:
go test -coverpkg=./... -coverprofile=cover.prof ./...
go tool cover -func cover.prof | grep -e "^total:"

.PHONY: fuzz
fuzz:
CGO_ENABLED=1 \
mkdir -p ./fuzzing && \
go run github.com/dvyukov/go-fuzz/go-fuzz-build -o=./fuzzing/gossip-fuzz.zip ./gossip && \
go run github.com/dvyukov/go-fuzz/go-fuzz -workdir=./fuzzing -bin=./fuzzing/gossip-fuzz.zip


.PHONY: clean
clean:
rm -fr ./build/*
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ ok github.com/Fantom-foundation/go-opera/gossip/filters 1.250s
ok github.com/Fantom-foundation/go-opera/integration 21.640s
```

Also it is tested with [fuzzing](./FUZZING.md).


### Operating a private network (fakenet)

Fakenet is a private network optimized for your private testing.
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.7.1
github.com/docker/docker v1.13.1
github.com/dvyukov/go-fuzz v0.0.0-20201127111758-49e582c6c23d
github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/ethereum/go-ethereum v1.9.22
github.com/evalphobia/logrus_sentry v0.8.2
Expand Down Expand Up @@ -49,10 +50,11 @@ require (
github.com/uber/jaeger-client-go v2.20.1+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible
go.uber.org/atomic v1.5.1 // indirect
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 // indirect
gopkg.in/urfave/cli.v1 v1.22.1
)

replace gopkg.in/urfave/cli.v1 => github.com/urfave/cli v1.20.0

replace github.com/ethereum/go-ethereum => github.com/uprendis/go-ethereum v1.9.22-ftm-0.2

replace github.com/dvyukov/go-fuzz => github.com/guzenok/go-fuzz v0.0.0-20210103140116-f9104dfb626f
6 changes: 3 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvK
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKxgp2ojts0FDgUA8dl62bmH0LxKanMoBr6MDTDM=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/guzenok/go-fuzz v0.0.0-20210103140116-f9104dfb626f h1:GQpcb9ywkaawJfZCFeIcrSWlsZFRTsig42e03dkzc+I=
github.com/guzenok/go-fuzz v0.0.0-20210103140116-f9104dfb626f/go.mod h1:Q5On640X2Z0YzKOijx9GVhUu/kvHnk9aKoWGMlRDMtc=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
Expand Down Expand Up @@ -356,7 +358,6 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -390,9 +391,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190912185636-87d9f09c5d89/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
Expand Down
21 changes: 2 additions & 19 deletions gossip/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (

"github.com/Fantom-foundation/lachesis-base/hash"
"github.com/Fantom-foundation/lachesis-base/inter/idx"
"github.com/Fantom-foundation/lachesis-base/kvdb/flushable"
"github.com/Fantom-foundation/lachesis-base/kvdb/memorydb"
"github.com/Fantom-foundation/lachesis-base/lachesis"
"github.com/Fantom-foundation/lachesis-base/utils/workers"
"github.com/ethereum/go-ethereum"
Expand All @@ -25,10 +23,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"

"github.com/Fantom-foundation/go-opera/evmcore"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/drivermodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/eventmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/evmmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/sealmodule"
"github.com/Fantom-foundation/go-opera/integration/makegenesis"
"github.com/Fantom-foundation/go-opera/inter"
"github.com/Fantom-foundation/go-opera/opera/genesis/gpos"
Expand Down Expand Up @@ -76,19 +70,8 @@ func newTestEnv() *testEnv {

genesis.Rules.Dag.MaxEpochDuration = inter.Timestamp(maxEpochDuration)

dbs := flushable.NewSyncedPool(
memorydb.NewProducer(""), []byte{0})
store := NewStore(dbs, LiteStoreConfig())
blockProc := BlockProc{
SealerModule: sealmodule.New(),
TxListenerModule: drivermodule.NewDriverTxListenerModule(),
GenesisTxTransactor: drivermodule.NewDriverTxGenesisTransactor(genesis),
PreTxTransactor: drivermodule.NewDriverTxPreTransactor(),
PostTxTransactor: drivermodule.NewDriverTxTransactor(),
EventsModule: eventmodule.New(),
EVMModule: evmmodule.New(),
}

store := NewMemStore()
blockProc := DefaultBlockProc(genesis)
_, err := store.ApplyGenesis(blockProc, genesis)
if err != nil {
panic(err)
Expand Down
145 changes: 145 additions & 0 deletions gossip/handler_fuzz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//+build gofuzz

package gossip

import (
"bytes"
"sync"

_ "github.com/dvyukov/go-fuzz/go-fuzz-defs"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"

"github.com/Fantom-foundation/go-opera/evmcore"
"github.com/Fantom-foundation/go-opera/integration/makegenesis"
"github.com/Fantom-foundation/go-opera/inter"
"github.com/Fantom-foundation/go-opera/utils"
)

const (
fuzzHot int = 1 // if the fuzzer should increase priority of the given input during subsequent fuzzing;
fuzzCold int = -1 // if the input must not be added to corpus even if gives new coverage;
fuzzNoMatter int = 0 // otherwise.
)

var (
fuzzedPM *ProtocolManager
)

func FuzzPM(data []byte) int {
var err error
if fuzzedPM == nil {
fuzzedPM, err = makeFuzzedPM()
if err != nil {
panic(err)
}
}

msg, err := newFuzzMsg(data)
if err != nil {
return fuzzCold
}
peer := p2p.NewPeer(enode.RandomID(enode.ID{}, 1), "fake-node-1", []p2p.Cap{})
input := &fuzzMsgReadWriter{msg}
other := fuzzedPM.newPeer(lachesis62, peer, input)

err = fuzzedPM.handleMsg(other)
if err != nil {
return fuzzNoMatter
}

return fuzzHot
}

func makeFuzzedPM() (pm *ProtocolManager, err error) {
const (
genesisStakers = 3
genesisBalance = 1e18
genesisStake = 2 * 4e6
)

genStore := makegenesis.FakeGenesisStore(genesisStakers, utils.ToFtm(genesisBalance), utils.ToFtm(genesisStake))
genesis := genStore.GetGenesis()

config := DefaultConfig()
store := NewMemStore()
blockProc := DefaultBlockProc(genesis)
_, err = store.ApplyGenesis(blockProc, genesis)
if err != nil {
return
}

var (
network = genesis.Rules
heavyCheckReader HeavyCheckReader
gasPowerCheckReader GasPowerCheckReader
// TODO: init
)

mu := new(sync.RWMutex)
feed := new(ServiceFeed)
checkers := makeCheckers(config.HeavyCheck, network.EvmChainConfig().ChainID, &heavyCheckReader, &gasPowerCheckReader, store)
processEvent := func(e *inter.EventPayload) error {
return nil
}

txpool := evmcore.NewTxPool(config.TxPool, network.EvmChainConfig(), &EvmStateReader{
ServiceFeed: feed,
store: store,
})

pm, err = NewProtocolManager(
config,
feed,
txpool,
mu,
checkers,
store,
processEvent,
nil)
if err != nil {
return
}

pm.Start(3)
return
}

type fuzzMsgReadWriter struct {
msg *p2p.Msg
}

func newFuzzMsg(data []byte) (*p2p.Msg, error) {
if len(data) < 1 {
return nil, ErrEmptyMessage
}

var (
codes = []uint64{
EthStatusMsg,
EvmTxMsg,
ProgressMsg,
NewEventIDsMsg,
GetEventsMsg,
EventsMsg,
RequestEventsStream,
EventsStreamResponse,
}
code = codes[int(data[0])%len(codes)]
)
data = data[1:]

return &p2p.Msg{
Code: code,
Size: uint32(len(data)),
Payload: bytes.NewReader(data),
}, nil
}

func (rw *fuzzMsgReadWriter) ReadMsg() (p2p.Msg, error) {
return *rw.msg, nil
}

func (rw *fuzzMsgReadWriter) WriteMsg(p2p.Msg) error {
return nil
}
17 changes: 17 additions & 0 deletions gossip/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ import (
"github.com/Fantom-foundation/go-opera/eventcheck/parentscheck"
"github.com/Fantom-foundation/go-opera/evmcore"
"github.com/Fantom-foundation/go-opera/gossip/blockproc"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/drivermodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/eventmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/evmmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/sealmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/verwatcher"
"github.com/Fantom-foundation/go-opera/gossip/emitter"
"github.com/Fantom-foundation/go-opera/gossip/filters"
"github.com/Fantom-foundation/go-opera/gossip/gasprice"
"github.com/Fantom-foundation/go-opera/inter"
"github.com/Fantom-foundation/go-opera/logger"
"github.com/Fantom-foundation/go-opera/opera"
"github.com/Fantom-foundation/go-opera/utils/wgmutex"
"github.com/Fantom-foundation/go-opera/valkeystore"
"github.com/Fantom-foundation/go-opera/vecmt"
Expand Down Expand Up @@ -83,6 +88,18 @@ type BlockProc struct {
EVMModule blockproc.EVM
}

func DefaultBlockProc(g opera.Genesis) BlockProc {
return BlockProc{
SealerModule: sealmodule.New(),
TxListenerModule: drivermodule.NewDriverTxListenerModule(),
GenesisTxTransactor: drivermodule.NewDriverTxGenesisTransactor(g),
PreTxTransactor: drivermodule.NewDriverTxPreTransactor(),
PostTxTransactor: drivermodule.NewDriverTxTransactor(),
EventsModule: eventmodule.New(),
EVMModule: evmmodule.New(),
}
}

// Service implements go-ethereum/node.Service interface.
type Service struct {
config Config
Expand Down
14 changes: 1 addition & 13 deletions integration/assembly.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ import (
"github.com/ethereum/go-ethereum/log"

"github.com/Fantom-foundation/go-opera/gossip"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/drivermodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/eventmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/evmmodule"
"github.com/Fantom-foundation/go-opera/gossip/blockproc/sealmodule"
"github.com/Fantom-foundation/go-opera/opera"
"github.com/Fantom-foundation/go-opera/opera/genesisstore"
"github.com/Fantom-foundation/go-opera/utils/adapters/vecmt2dagidx"
Expand Down Expand Up @@ -84,15 +80,7 @@ func rawApplyGenesis(gdb *gossip.Store, cdb *abft.Store, g opera.Genesis, cfg Co
}

func rawMakeEngine(gdb *gossip.Store, cdb *abft.Store, g opera.Genesis, cfg Configs, applyGenesis bool) (*abft.Lachesis, *vecmt.Index, gossip.BlockProc, error) {
blockProc := gossip.BlockProc{
SealerModule: sealmodule.New(),
TxListenerModule: drivermodule.NewDriverTxListenerModule(),
GenesisTxTransactor: drivermodule.NewDriverTxGenesisTransactor(g),
PreTxTransactor: drivermodule.NewDriverTxPreTransactor(),
PostTxTransactor: drivermodule.NewDriverTxTransactor(),
EventsModule: eventmodule.New(),
EVMModule: evmmodule.New(),
}
blockProc := gossip.DefaultBlockProc(g)

err := gdb.Migrate()
if err != nil {
Expand Down