Skip to content

Commit

Permalink
p2p: add peer to peer fuzzing support (#2426)
Browse files Browse the repository at this point in the history
Adds support for peer to peer fuzzing by adding a fuzz reader writer which intercepts all the libp2p messages and reads/writes fuzzed data.

category: feature
ticket: #2375
  • Loading branch information
dB2510 authored Aug 1, 2023
1 parent 79fe7a6 commit 4f12174
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 8 deletions.
9 changes: 8 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ type Config struct {
SyntheticBlockProposals bool
BuilderAPI bool
SimnetBMockFuzz bool
CharonP2PFuzz bool

TestConfig TestConfig
}
Expand Down Expand Up @@ -117,6 +116,9 @@ type TestConfig struct {
TCPNodeCallback func(host.Host)
// LibP2POpts provide test specific libp2p options.
LibP2POpts []libp2p.Option
// P2PFuzz enables peer to peer fuzzing of charon nodes in a cluster.
// If enabled, this node will send fuzzed data over p2p to its peers in the cluster.
P2PFuzz bool
}

// Run is the entrypoint for running a charon DVC instance.
Expand Down Expand Up @@ -222,6 +224,11 @@ func Run(ctx context.Context, conf Config) (err error) {
return err
}

// Enable p2p fuzzing if --p2p-fuzz is set.
if conf.TestConfig.P2PFuzz {
p2p.SetFuzzerDefaultsUnsafe()
}

sender := new(p2p.Sender)

wirePeerInfo(life, tcpNode, peerIDs, cluster.InitialMutationHash, sender)
Expand Down
6 changes: 4 additions & 2 deletions cmd/cmd_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func TestCmdFlags(t *testing.T) {
},
{
Name: "unsafe run",
Args: slice("unsafe", "run", "--charon-p2p-fuzz=true"),
Args: slice("unsafe", "run", "--p2p-fuzz=true"),
Envs: map[string]string{
"CHARON_BEACON_NODE_ENDPOINTS": "http://beacon.node",
},
Expand Down Expand Up @@ -135,7 +135,9 @@ func TestCmdFlags(t *testing.T) {
BeaconNodeAddrs: []string{"http://beacon.node"},
JaegerAddr: "",
JaegerService: "charon",
CharonP2PFuzz: true,
TestConfig: app.TestConfig{
P2PFuzz: true,
},
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func bindRunFlags(cmd *cobra.Command, config *app.Config) {

// TODO(dhruv): add more test only flags to this function.
func bindUnsafeRunFlags(cmd *cobra.Command, config *app.Config) {
cmd.Flags().BoolVar(&config.CharonP2PFuzz, "charon-p2p-fuzz", false, "Configures charon p2p network to send fuzzed data to its peers.")
cmd.Flags().BoolVar(&config.TestConfig.P2PFuzz, "p2p-fuzz", false, "Configures charon to send fuzzed data via p2p network to its peers.")
}

func bindPrivKeyFlag(cmd *cobra.Command, privKeyFile *string, privkeyLockEnabled *bool) {
Expand Down
2 changes: 0 additions & 2 deletions core/consensus/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ func (t *transport) Broadcast(ctx context.Context, typ qbft.MsgType, duty core.D
}

// ProcessReceives processes received messages from the outer buffer until the context is closed.
//

func (t *transport) ProcessReceives(ctx context.Context, outerBuffer chan msg) {
for {
select {
Expand Down
35 changes: 35 additions & 0 deletions p2p/fuzz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright © 2022-2023 Obol Labs Inc. Licensed under the terms of a Business Source License 1.1

package p2p

import (
fuzz "github.com/google/gofuzz"
"github.com/libp2p/go-msgio/pbio"
"google.golang.org/protobuf/proto"
)

var (
_ pbio.Reader = fuzzReaderWriter{}
_ pbio.Writer = fuzzReaderWriter{}
)

// fuzzReaderWriter implements the pbio.Reader and pbio.Writer interfaces and provides functionality
// for reading and writing messages with fuzzed data.
type fuzzReaderWriter struct {
w pbio.Writer
}

// ReadMsg fuzzes the received message, and stores the fuzzed data in the provided `msg` argument.
func (fuzzReaderWriter) ReadMsg(msg proto.Message) error {
fuzz.New().Fuzz(msg)

return nil
}

// WriteMsg writes the fuzzed message using the associated writer.
func (f fuzzReaderWriter) WriteMsg(msg proto.Message) error {
cloneMsg := proto.Clone(msg)
fuzz.New().Fuzz(cloneMsg)

return f.w.WriteMsg(cloneMsg)
}
23 changes: 21 additions & 2 deletions p2p/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const (
maxMsgSize = 128 << 20 // 128MB
)

var (
defaultWriterFunc = func(s network.Stream) pbio.Writer { return legacyReadWriter{s} }
defaultReaderFunc = func(s network.Stream) pbio.Reader { return legacyReadWriter{s} }
)

// SendFunc is an abstract function responsible for sending libp2p messages.
type SendFunc func(context.Context, host.Host, protocol.ID, peer.ID, proto.Message, ...SendRecvOption) error

Expand Down Expand Up @@ -165,15 +170,29 @@ func WithDelimitedProtocol(pID protocol.ID) func(*sendRecvOpts) {
}
}

// SetFuzzerDefaultsUnsafe sets default reader and writer functions to fuzzed versions of the same if p2p fuzz is enabled.
//
// The fuzzReaderWriter is responsible for creating a customized reader and writer for each network stream
// associated with a specific protocol. The reader and writer implement the pbio.Reader and pbio.Writer interfaces respectively
// respectively, from the "pbio" package.
func SetFuzzerDefaultsUnsafe() {
defaultWriterFunc = func(s network.Stream) pbio.Writer {
return fuzzReaderWriter{w: pbio.NewDelimitedWriter(s)}
}
defaultReaderFunc = func(s network.Stream) pbio.Reader {
return fuzzReaderWriter{}
}
}

// defaultSendRecvOpts returns the default sendRecvOpts, it uses the legacy writers and noop rtt callback.
func defaultSendRecvOpts(pID protocol.ID) sendRecvOpts {
return sendRecvOpts{
protocols: []protocol.ID{pID},
writersByProtocol: map[protocol.ID]func(s network.Stream) pbio.Writer{
pID: func(s network.Stream) pbio.Writer { return legacyReadWriter{s} },
pID: defaultWriterFunc,
},
readersByProtocol: map[protocol.ID]func(s network.Stream) pbio.Reader{
pID: func(s network.Stream) pbio.Reader { return legacyReadWriter{s} },
pID: defaultReaderFunc,
},
rttCallback: func(time.Duration) {},
}
Expand Down

0 comments on commit 4f12174

Please sign in to comment.