File: fuzz.go

package info (click to toggle)
tendermint-go-p2p 0.0~git20170113.0.3d98f67-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 248 kB
  • ctags: 425
  • sloc: sh: 23; makefile: 4
file content (141 lines) | stat: -rw-r--r-- 3,297 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package p2p

import (
	"math/rand"
	"net"
	"sync"
	"time"

	cfg "github.com/tendermint/go-config"
)

//--------------------------------------------------------
// delay reads/writes
// randomly drop reads/writes
// randomly drop connections

const (
	FuzzModeDrop  = "drop"
	FuzzModeDelay = "delay"
)

func FuzzConn(config cfg.Config, conn net.Conn) net.Conn {
	return &FuzzedConnection{
		conn:   conn,
		start:  time.After(time.Second * 10), // so we have time to do peer handshakes and get set up
		params: config,
	}
}

type FuzzedConnection struct {
	conn net.Conn

	mtx   sync.Mutex
	fuzz  bool // we don't start fuzzing right away
	start <-chan time.Time

	// fuzz params
	params cfg.Config
}

func (fc *FuzzedConnection) randomDuration() time.Duration {
	return time.Millisecond * time.Duration(rand.Int()%fc.MaxDelayMilliseconds())
}

func (fc *FuzzedConnection) Active() bool {
	return fc.params.GetBool(configFuzzActive)
}

func (fc *FuzzedConnection) Mode() string {
	return fc.params.GetString(configFuzzMode)
}

func (fc *FuzzedConnection) ProbDropRW() float64 {
	return fc.params.GetFloat64(configFuzzProbDropRW)
}

func (fc *FuzzedConnection) ProbDropConn() float64 {
	return fc.params.GetFloat64(configFuzzProbDropConn)
}

func (fc *FuzzedConnection) ProbSleep() float64 {
	return fc.params.GetFloat64(configFuzzProbSleep)
}

func (fc *FuzzedConnection) MaxDelayMilliseconds() int {
	return fc.params.GetInt(configFuzzMaxDelayMilliseconds)
}

// implements the fuzz (delay, kill conn)
// and returns whether or not the read/write should be ignored
func (fc *FuzzedConnection) Fuzz() bool {
	if !fc.shouldFuzz() {
		return false
	}

	switch fc.Mode() {
	case FuzzModeDrop:
		// randomly drop the r/w, drop the conn, or sleep
		r := rand.Float64()
		if r <= fc.ProbDropRW() {
			return true
		} else if r < fc.ProbDropRW()+fc.ProbDropConn() {
			// XXX: can't this fail because machine precision?
			// XXX: do we need an error?
			fc.Close()
			return true
		} else if r < fc.ProbDropRW()+fc.ProbDropConn()+fc.ProbSleep() {
			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 {
	if !fc.Active() {
		return false
	}

	fc.mtx.Lock()
	defer fc.mtx.Unlock()
	if fc.fuzz {
		return true
	}

	select {
	case <-fc.start:
		fc.fuzz = true
	default:
	}
	return false
}

func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
	if fc.Fuzz() {
		return 0, nil
	}
	return fc.conn.Read(data)
}

func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
	if fc.Fuzz() {
		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)
}