tendermint/fuzz.go

191 lines
4.4 KiB
Go
Raw Normal View History

2015-12-22 18:58:11 -08:00
package p2p
import (
2017-04-11 08:47:05 -07:00
"fmt"
2015-12-22 18:58:11 -08:00
"math/rand"
"net"
"sync"
"time"
)
const (
2017-04-11 08:47:05 -07:00
// FuzzModeDrop is a mode in which we randomly drop reads/writes, connections or sleep
FuzzModeDrop = iota
// FuzzModeDelay is a mode in which we randomly sleep
FuzzModeDelay
2015-12-22 18:58:11 -08:00
)
2017-04-11 08:47:05 -07:00
type FuzzConnConfig struct {
Mode int
MaxDelay time.Duration
ProbDropRW float64
ProbDropConn float64
ProbSleep float64
}
func defaultFuzzConnConfig() *FuzzConnConfig {
return &FuzzConnConfig{
Mode: FuzzModeDrop,
MaxDelay: 3 * time.Second,
ProbDropRW: 0.2,
ProbDropConn: 0.00,
ProbSleep: 0.00,
}
}
func FuzzConn(conn net.Conn) net.Conn {
return FuzzConnFromConfig(conn, defaultFuzzConnConfig())
}
func FuzzConnFromConfig(conn net.Conn, config *FuzzConnConfig) net.Conn {
return &FuzzedConnection{
conn: conn,
start: make(<-chan time.Time),
active: true,
mode: config.Mode,
maxDelay: config.MaxDelay,
probDropRW: config.ProbDropRW,
probDropConn: config.ProbDropConn,
probSleep: config.ProbSleep,
}
}
func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn {
return FuzzConnAfterFromConfig(conn, d, defaultFuzzConnConfig())
}
func FuzzConnAfterFromConfig(conn net.Conn, d time.Duration, config *FuzzConnConfig) net.Conn {
2015-12-22 18:58:11 -08:00
return &FuzzedConnection{
2017-04-11 08:47:05 -07:00
conn: conn,
start: time.After(d),
active: false,
mode: config.Mode,
maxDelay: config.MaxDelay,
probDropRW: config.ProbDropRW,
probDropConn: config.ProbDropConn,
probSleep: config.ProbSleep,
2015-12-22 18:58:11 -08:00
}
}
2017-04-11 08:47:05 -07:00
// FuzzedConnection wraps any net.Conn and depending on the mode either delays
// reads/writes or randomly drops reads/writes/connections.
2015-12-22 18:58:11 -08:00
type FuzzedConnection struct {
conn net.Conn
2017-04-11 08:47:05 -07:00
mtx sync.Mutex
start <-chan time.Time
active bool
2015-12-22 18:58:11 -08:00
2017-04-11 08:47:05 -07:00
mode int
maxDelay time.Duration
probDropRW float64
probDropConn float64
probSleep float64
2015-12-22 18:58:11 -08:00
}
func (fc *FuzzedConnection) randomDuration() time.Duration {
2017-04-11 08:47:05 -07:00
maxDelayMillis := int(fc.maxDelay.Nanoseconds() / 1000)
return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis)
2015-12-22 18:58:11 -08:00
}
2017-04-11 08:47:05 -07:00
func (fc *FuzzedConnection) SetMode(mode int) {
switch mode {
case FuzzModeDrop:
fc.mode = FuzzModeDrop
case FuzzModeDelay:
fc.mode = FuzzModeDelay
default:
panic(fmt.Sprintf("Unknown mode %d", mode))
}
2015-12-22 18:58:11 -08:00
}
2017-04-11 08:47:05 -07:00
func (fc *FuzzedConnection) SetProbDropRW(prob float64) {
fc.probDropRW = prob
2015-12-22 18:58:11 -08:00
}
2017-04-11 08:47:05 -07:00
func (fc *FuzzedConnection) SetProbDropConn(prob float64) {
fc.probDropConn = prob
2015-12-22 18:58:11 -08:00
}
2017-04-11 08:47:05 -07:00
func (fc *FuzzedConnection) SetProbSleep(prob float64) {
fc.probSleep = prob
2015-12-22 18:58:11 -08:00
}
2017-04-11 08:47:05 -07:00
func (fc *FuzzedConnection) SetMaxDelay(d time.Duration) {
fc.maxDelay = d
2015-12-22 18:58:11 -08:00
}
// implements the fuzz (delay, kill conn)
// and returns whether or not the read/write should be ignored
2017-04-11 08:47:05 -07:00
func (fc *FuzzedConnection) fuzz() bool {
2015-12-22 18:58:11 -08:00
if !fc.shouldFuzz() {
return false
}
2017-04-11 08:47:05 -07:00
switch fc.mode {
2015-12-22 18:58:11 -08:00
case FuzzModeDrop:
// randomly drop the r/w, drop the conn, or sleep
r := rand.Float64()
2017-04-11 08:47:05 -07:00
if r <= fc.probDropRW {
2015-12-22 18:58:11 -08:00
return true
2017-04-11 08:47:05 -07:00
} else if r < fc.probDropRW+fc.probDropConn {
2015-12-22 18:58:11 -08:00
// XXX: can't this fail because machine precision?
// XXX: do we need an error?
fc.Close()
return true
2017-04-11 08:47:05 -07:00
} else if r < fc.probDropRW+fc.probDropConn+fc.probSleep {
2015-12-22 18:58:11 -08:00
time.Sleep(fc.randomDuration())
}
case FuzzModeDelay:
// sleep a bit
time.Sleep(fc.randomDuration())
}
return false
}
// we don't fuzz until start chan fires
func (fc *FuzzedConnection) shouldFuzz() bool {
2017-04-11 08:47:05 -07:00
if fc.active {
return true
2015-12-22 18:58:11 -08:00
}
fc.mtx.Lock()
defer fc.mtx.Unlock()
select {
case <-fc.start:
2017-04-11 08:47:05 -07:00
fc.active = true
return true
2015-12-22 18:58:11 -08:00
default:
2017-04-11 08:47:05 -07:00
return false
2015-12-22 18:58:11 -08:00
}
}
2017-04-11 08:47:05 -07:00
// Read implements net.Conn
2015-12-22 18:58:11 -08:00
func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
2017-04-11 08:47:05 -07:00
if fc.fuzz() {
2015-12-22 18:58:11 -08:00
return 0, nil
}
return fc.conn.Read(data)
}
2017-04-11 08:47:05 -07:00
// Write implements net.Conn
2015-12-22 18:58:11 -08:00
func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
2017-04-11 08:47:05 -07:00
if fc.fuzz() {
2015-12-22 18:58:11 -08:00
return 0, nil
}
return fc.conn.Write(data)
}
// Implements net.Conn
func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
return fc.conn.SetReadDeadline(t)
}
func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
return fc.conn.SetWriteDeadline(t)
}