12
12
#include < time.h>
13
13
#include < util/asmap.h>
14
14
15
+ #include < cassert>
15
16
#include < cstdint>
16
17
#include < optional>
17
18
#include < string>
@@ -25,25 +26,208 @@ void initialize_addrman()
25
26
class CAddrManDeterministic : public CAddrMan
26
27
{
27
28
public:
28
- void MakeDeterministic (const uint256& random_seed)
29
+ FuzzedDataProvider& m_fuzzed_data_provider;
30
+
31
+ explicit CAddrManDeterministic (FuzzedDataProvider& fuzzed_data_provider)
32
+ : m_fuzzed_data_provider(fuzzed_data_provider)
33
+ {
34
+ WITH_LOCK (cs, insecure_rand = FastRandomContext{ConsumeUInt256 (fuzzed_data_provider)});
35
+ if (fuzzed_data_provider.ConsumeBool ()) {
36
+ m_asmap = ConsumeRandomLengthBitVector (fuzzed_data_provider);
37
+ if (!SanityCheckASMap (m_asmap)) {
38
+ m_asmap.clear ();
39
+ }
40
+ }
41
+ }
42
+
43
+ /* *
44
+ * Generate a random address. Always returns a valid address.
45
+ */
46
+ CNetAddr RandAddr () EXCLUSIVE_LOCKS_REQUIRED(cs)
47
+ {
48
+ CNetAddr addr;
49
+ if (m_fuzzed_data_provider.remaining_bytes () > 1 && m_fuzzed_data_provider.ConsumeBool ()) {
50
+ addr = ConsumeNetAddr (m_fuzzed_data_provider);
51
+ } else {
52
+ // The networks [1..6] correspond to CNetAddr::BIP155Network (private).
53
+ static const std::map<uint8_t , uint8_t > net_len_map = {{1 , ADDR_IPV4_SIZE},
54
+ {2 , ADDR_IPV6_SIZE},
55
+ {4 , ADDR_TORV3_SIZE},
56
+ {5 , ADDR_I2P_SIZE},
57
+ {6 , ADDR_CJDNS_SIZE}};
58
+ uint8_t net = insecure_rand.randrange (5 ) + 1 ; // [1..5]
59
+ if (net == 3 ) {
60
+ net = 6 ;
61
+ }
62
+
63
+ CDataStream s (SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
64
+
65
+ s << net;
66
+ s << insecure_rand.randbytes (net_len_map.at (net));
67
+
68
+ s >> addr;
69
+ }
70
+
71
+ // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
72
+ if (!addr.IsValid ()) {
73
+ in_addr v4_addr = {};
74
+ v4_addr.s_addr = 0x05050505 ;
75
+ addr = CNetAddr{v4_addr};
76
+ }
77
+
78
+ return addr;
79
+ }
80
+
81
+ /* *
82
+ * Fill this addrman with lots of addresses from lots of sources.
83
+ */
84
+ void Fill ()
29
85
{
30
- WITH_LOCK (cs, insecure_rand = FastRandomContext{random_seed});
31
- Clear ();
86
+ LOCK (cs);
87
+
88
+ // Add some of the addresses directly to the "tried" table.
89
+
90
+ // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
91
+ const size_t n = m_fuzzed_data_provider.ConsumeIntegralInRange <size_t >(0 , 3 );
92
+
93
+ const size_t num_sources = m_fuzzed_data_provider.ConsumeIntegralInRange <size_t >(10 , 50 );
94
+ CNetAddr prev_source;
95
+ // Use insecure_rand inside the loops instead of m_fuzzed_data_provider because when
96
+ // the latter is exhausted it just returns 0.
97
+ for (size_t i = 0 ; i < num_sources; ++i) {
98
+ const auto source = RandAddr ();
99
+ const size_t num_addresses = insecure_rand.randrange (500 ) + 1 ; // [1..500]
100
+
101
+ for (size_t j = 0 ; j < num_addresses; ++j) {
102
+ const auto addr = CAddress{CService{RandAddr (), 8333 }, NODE_NETWORK};
103
+ const auto time_penalty = insecure_rand.randrange (100000001 );
104
+ #if 1
105
+ // 2.83 sec to fill.
106
+ if (n > 0 && mapInfo.size () % n == 0 && mapAddr.find (addr) == mapAddr.end ()) {
107
+ // Add to the "tried" table (if the bucket slot is free).
108
+ const CAddrInfo dummy{addr, source};
109
+ const int bucket = dummy.GetTriedBucket (nKey, m_asmap);
110
+ const int bucket_pos = dummy.GetBucketPosition (nKey, false , bucket);
111
+ if (vvTried[bucket][bucket_pos] == -1 ) {
112
+ int id;
113
+ CAddrInfo* addr_info = Create (addr, source, &id);
114
+ vvTried[bucket][bucket_pos] = id;
115
+ addr_info->fInTried = true ;
116
+ ++nTried;
117
+ }
118
+ } else {
119
+ // Add to the "new" table.
120
+ Add_ (addr, source, time_penalty);
121
+ }
122
+ #else
123
+ // 261.91 sec to fill.
124
+ Add_(addr, source, time_penalty);
125
+ if (n > 0 && mapInfo.size() % n == 0) {
126
+ Good_(addr, false, GetTime());
127
+ }
128
+ #endif
129
+ // Add 10% of the addresses from more than one source.
130
+ if (insecure_rand.randrange (10 ) == 0 && prev_source.IsValid ()) {
131
+ Add_ (addr, prev_source, time_penalty);
132
+ }
133
+ }
134
+ prev_source = source;
135
+ }
136
+ }
137
+
138
+ /* *
139
+ * Compare with another AddrMan.
140
+ * This compares:
141
+ * - the values in `mapInfo` (the keys aka ids are ignored)
142
+ * - vvNew entries refer to the same addresses
143
+ * - vvTried entries refer to the same addresses
144
+ */
145
+ bool operator ==(const CAddrManDeterministic& other)
146
+ {
147
+ LOCK2 (cs, other.cs );
148
+
149
+ if (mapInfo.size () != other.mapInfo .size () || nNew != other.nNew ||
150
+ nTried != other.nTried ) {
151
+ return false ;
152
+ }
153
+
154
+ // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
155
+ // Keys may be different.
156
+
157
+ using CAddrInfoHasher = std::function<size_t (const CAddrInfo&)>;
158
+ using CAddrInfoEq = std::function<bool (const CAddrInfo&, const CAddrInfo&)>;
159
+
160
+ CNetAddrHash netaddr_hasher;
161
+
162
+ CAddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const CAddrInfo& a) {
163
+ return netaddr_hasher (static_cast <CNetAddr>(a)) ^ netaddr_hasher (a.source ) ^
164
+ a.nLastSuccess ^ a.nAttempts ^ a.nRefCount ^ a.fInTried ;
165
+ };
166
+
167
+ CAddrInfoEq addrinfo_eq = [](const CAddrInfo& lhs, const CAddrInfo& rhs) {
168
+ return static_cast <CNetAddr>(lhs) == static_cast <CNetAddr>(rhs) &&
169
+ lhs.source == rhs.source && lhs.nLastSuccess == rhs.nLastSuccess &&
170
+ lhs.nAttempts == rhs.nAttempts && lhs.nRefCount == rhs.nRefCount &&
171
+ lhs.fInTried == rhs.fInTried ;
172
+ };
173
+
174
+ using Addresses = std::unordered_set<CAddrInfo, CAddrInfoHasher, CAddrInfoEq>;
175
+
176
+ const size_t num_addresses{mapInfo.size ()};
177
+
178
+ Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
179
+ for (const auto & [id, addr] : mapInfo) {
180
+ addresses.insert (addr);
181
+ }
182
+
183
+ Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
184
+ for (const auto & [id, addr] : other.mapInfo ) {
185
+ other_addresses.insert (addr);
186
+ }
187
+
188
+ if (addresses != other_addresses) {
189
+ return false ;
190
+ }
191
+
192
+ auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED (cs, other.cs ) {
193
+ if (id == -1 && other_id == -1 ) {
194
+ return true ;
195
+ }
196
+ if ((id == -1 && other_id != -1 ) || (id != -1 && other_id == -1 )) {
197
+ return false ;
198
+ }
199
+ return mapInfo.at (id) == other.mapInfo .at (other_id);
200
+ };
201
+
202
+ // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
203
+ // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
204
+ // themselves may differ between `vvNew` and `other.vvNew`.
205
+ for (size_t i = 0 ; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
206
+ for (size_t j = 0 ; j < ADDRMAN_BUCKET_SIZE; ++j) {
207
+ if (!IdsReferToSameAddress (vvNew[i][j], other.vvNew [i][j])) {
208
+ return false ;
209
+ }
210
+ }
211
+ }
212
+
213
+ // Same for `vvTried`.
214
+ for (size_t i = 0 ; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
215
+ for (size_t j = 0 ; j < ADDRMAN_BUCKET_SIZE; ++j) {
216
+ if (!IdsReferToSameAddress (vvTried[i][j], other.vvTried [i][j])) {
217
+ return false ;
218
+ }
219
+ }
220
+ }
221
+
222
+ return true ;
32
223
}
33
224
};
34
225
35
226
FUZZ_TARGET_INIT (addrman, initialize_addrman)
36
227
{
37
228
FuzzedDataProvider fuzzed_data_provider (buffer.data (), buffer.size ());
38
229
SetMockTime (ConsumeTime (fuzzed_data_provider));
39
- CAddrManDeterministic addr_man;
40
- addr_man.MakeDeterministic (ConsumeUInt256 (fuzzed_data_provider));
41
- if (fuzzed_data_provider.ConsumeBool ()) {
42
- addr_man.m_asmap = ConsumeRandomLengthBitVector (fuzzed_data_provider);
43
- if (!SanityCheckASMap (addr_man.m_asmap )) {
44
- addr_man.m_asmap .clear ();
45
- }
46
- }
230
+ CAddrManDeterministic addr_man{fuzzed_data_provider};
47
231
if (fuzzed_data_provider.ConsumeBool ()) {
48
232
const std::vector<uint8_t > serialized_data{ConsumeRandomLengthByteVector (fuzzed_data_provider)};
49
233
CDataStream ds (serialized_data, SER_DISK, INIT_PROTO_VERSION);
@@ -123,3 +307,21 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
123
307
CDataStream data_stream (SER_NETWORK, PROTOCOL_VERSION);
124
308
data_stream << const_addr_man;
125
309
}
310
+
311
+ // Check that serialize followed by unserialize produces the same addrman.
312
+ FUZZ_TARGET_INIT (addrman_serdeser, initialize_addrman)
313
+ {
314
+ FuzzedDataProvider fuzzed_data_provider (buffer.data (), buffer.size ());
315
+ SetMockTime (ConsumeTime (fuzzed_data_provider));
316
+
317
+ CAddrManDeterministic addr_man1{fuzzed_data_provider};
318
+ CAddrManDeterministic addr_man2{fuzzed_data_provider};
319
+ addr_man2.m_asmap = addr_man1.m_asmap ;
320
+
321
+ CDataStream data_stream (SER_NETWORK, PROTOCOL_VERSION);
322
+
323
+ addr_man1.Fill ();
324
+ data_stream << addr_man1;
325
+ data_stream >> addr_man2;
326
+ assert (addr_man1 == addr_man2);
327
+ }
0 commit comments