Skip to content

Commit 9a7206a

Browse files
committed
Merge bitcoin/bitcoin#29536: fuzz: fuzz connman with non-empty addrman + ASMap
552cae2 fuzz: cover `ASMapHealthCheck` in connman target (brunoerg) 33b0f3a fuzz: use `ConsumeNetGroupManager` in connman target (brunoerg) 18c8a09 fuzz: move `ConsumeNetGroupManager` to util (brunoerg) fe62463 fuzz: fuzz `connman` with a non-empty addrman (brunoerg) 0a12cff fuzz: move `AddrManDeterministic` to util (brunoerg) Pull request description: ### Motivation Currently, we fuzz connman with an addrman from `NodeContext`. However, fuzzing connman with only empty addrman might not be effective, especially for functions like `GetAddresses` and other ones that plays with addrman. Also, we do not fuzz connman with ASMap, what would be good for functions that need `GetGroup`, or even for addrman. Without it, I do not see how effective would be fuzzing `ASMapHealthCheck`, for example. ### Changes - Move `AddrManDeterministic` and `ConsumeNetGroupManager` to util. - Use `ConsumeNetGroupManager` in connman target to construct a netgroupmanager and use it for `ConnmanTestMsg`. - Use `AddrManDeterministic` in connman target to create an addrman. It does not slow down as "filling" the addrman (e.g. with `FillAddrman`). - Add coverage for `ASMapHealthCheck`. ACKs for top commit: maflcko: review ACK 552cae2 🏀 dergoegge: Code review ACK 552cae2 marcofleon: Code review ACK 552cae2. Changes match the PR description. Tree-SHA512: ba861c839602054077e4bf3649763eeb48357cda83ca3ddd32b02a1b61f4e44a0c5070182f001f9bf531d0d64717876279a7de3ddb9de028b343533b89233851
2 parents d4abaf8 + 552cae2 commit 9a7206a

File tree

3 files changed

+137
-115
lines changed

3 files changed

+137
-115
lines changed

src/test/fuzz/addrman.cpp

+4-113
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,6 @@ void initialize_addrman()
3939
g_setup = testing_setup.get();
4040
}
4141

42-
[[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept
43-
{
44-
std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
45-
if (!SanityCheckASMap(asmap, 128)) asmap.clear();
46-
return NetGroupManager(asmap);
47-
}
48-
4942
FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman)
5043
{
5144
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
@@ -118,121 +111,19 @@ void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)
118111
}
119112
}
120113

121-
class AddrManDeterministic : public AddrMan
122-
{
123-
public:
124-
explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider)
125-
: AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio())
126-
{
127-
WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider)));
128-
}
129-
130-
/**
131-
* Compare with another AddrMan.
132-
* This compares:
133-
* - the values in `mapInfo` (the keys aka ids are ignored)
134-
* - vvNew entries refer to the same addresses
135-
* - vvTried entries refer to the same addresses
136-
*/
137-
bool operator==(const AddrManDeterministic& other) const
138-
{
139-
LOCK2(m_impl->cs, other.m_impl->cs);
140-
141-
if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
142-
m_impl->nTried != other.m_impl->nTried) {
143-
return false;
144-
}
145-
146-
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
147-
// Keys may be different.
148-
149-
auto addrinfo_hasher = [](const AddrInfo& a) {
150-
CSipHasher hasher(0, 0);
151-
auto addr_key = a.GetKey();
152-
auto source_key = a.source.GetAddrBytes();
153-
hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success));
154-
hasher.Write(a.nAttempts);
155-
hasher.Write(a.nRefCount);
156-
hasher.Write(a.fInTried);
157-
hasher.Write(a.GetNetwork());
158-
hasher.Write(a.source.GetNetwork());
159-
hasher.Write(addr_key.size());
160-
hasher.Write(source_key.size());
161-
hasher.Write(addr_key);
162-
hasher.Write(source_key);
163-
return (size_t)hasher.Finalize();
164-
};
165-
166-
auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
167-
return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
168-
std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
169-
};
170-
171-
using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
172-
173-
const size_t num_addresses{m_impl->mapInfo.size()};
174-
175-
Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
176-
for (const auto& [id, addr] : m_impl->mapInfo) {
177-
addresses.insert(addr);
178-
}
179-
180-
Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
181-
for (const auto& [id, addr] : other.m_impl->mapInfo) {
182-
other_addresses.insert(addr);
183-
}
184-
185-
if (addresses != other_addresses) {
186-
return false;
187-
}
188-
189-
auto IdsReferToSameAddress = [&](nid_type id, nid_type other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
190-
if (id == -1 && other_id == -1) {
191-
return true;
192-
}
193-
if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
194-
return false;
195-
}
196-
return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
197-
};
198-
199-
// Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
200-
// contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
201-
// themselves may differ between `vvNew` and `other.vvNew`.
202-
for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
203-
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
204-
if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
205-
return false;
206-
}
207-
}
208-
}
209-
210-
// Same for `vvTried`.
211-
for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
212-
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
213-
if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
214-
return false;
215-
}
216-
}
217-
}
218-
219-
return true;
220-
}
221-
};
222-
223114
FUZZ_TARGET(addrman, .init = initialize_addrman)
224115
{
225116
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
226117
SetMockTime(ConsumeTime(fuzzed_data_provider));
227118
NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
228-
auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
119+
auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
229120
if (fuzzed_data_provider.ConsumeBool()) {
230121
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
231122
DataStream ds{serialized_data};
232123
try {
233124
ds >> *addr_man_ptr;
234125
} catch (const std::ios_base::failure&) {
235-
addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
126+
addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
236127
}
237128
}
238129
AddrManDeterministic& addr_man = *addr_man_ptr;
@@ -310,8 +201,8 @@ FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman)
310201
SetMockTime(ConsumeTime(fuzzed_data_provider));
311202

312203
NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
313-
AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider};
314-
AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider};
204+
AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider, GetCheckRatio()};
205+
AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider, GetCheckRatio()};
315206

316207
DataStream data_stream{};
317208

src/test/fuzz/connman.cpp

+21-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020

2121
namespace {
2222
const TestingSetup* g_setup;
23+
24+
int32_t GetCheckRatio()
25+
{
26+
return std::clamp<int32_t>(g_setup->m_node.args->GetIntArg("-checkaddrman", 0), 0, 1000000);
27+
}
28+
2329
} // namespace
2430

2531
void initialize_connman()
@@ -32,10 +38,22 @@ FUZZ_TARGET(connman, .init = initialize_connman)
3238
{
3339
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
3440
SetMockTime(ConsumeTime(fuzzed_data_provider));
41+
auto netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
42+
auto addr_man_ptr{std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio())};
43+
if (fuzzed_data_provider.ConsumeBool()) {
44+
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
45+
DataStream ds{serialized_data};
46+
try {
47+
ds >> *addr_man_ptr;
48+
} catch (const std::ios_base::failure&) {
49+
addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
50+
}
51+
}
52+
AddrManDeterministic& addr_man{*addr_man_ptr};
3553
ConnmanTestMsg connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
3654
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
37-
*g_setup->m_node.addrman,
38-
*g_setup->m_node.netgroupman,
55+
addr_man,
56+
netgroupman,
3957
Params(),
4058
fuzzed_data_provider.ConsumeBool()};
4159

@@ -140,6 +158,7 @@ FUZZ_TARGET(connman, .init = initialize_connman)
140158
(void)connman.GetTotalBytesSent();
141159
(void)connman.GetTryNewOutboundPeer();
142160
(void)connman.GetUseAddrmanOutgoing();
161+
(void)connman.ASMapHealthCheck();
143162

144163
connman.ClearTestNodes();
145164
}

src/test/fuzz/util/net.h

+112
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef BITCOIN_TEST_FUZZ_UTIL_NET_H
66
#define BITCOIN_TEST_FUZZ_UTIL_NET_H
77

8+
#include <addrman.h>
9+
#include <addrman_impl.h>
810
#include <net.h>
911
#include <net_permissions.h>
1012
#include <netaddress.h>
@@ -15,6 +17,7 @@
1517
#include <test/fuzz/util.h>
1618
#include <test/util/net.h>
1719
#include <threadsafety.h>
20+
#include <util/asmap.h>
1821
#include <util/sock.h>
1922

2023
#include <chrono>
@@ -34,6 +37,108 @@
3437
*/
3538
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext* rand = nullptr) noexcept;
3639

40+
class AddrManDeterministic : public AddrMan
41+
{
42+
public:
43+
explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider, int32_t check_ratio)
44+
: AddrMan(netgroupman, /*deterministic=*/true, check_ratio)
45+
{
46+
WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider)));
47+
}
48+
49+
/**
50+
* Compare with another AddrMan.
51+
* This compares:
52+
* - the values in `mapInfo` (the keys aka ids are ignored)
53+
* - vvNew entries refer to the same addresses
54+
* - vvTried entries refer to the same addresses
55+
*/
56+
bool operator==(const AddrManDeterministic& other) const
57+
{
58+
LOCK2(m_impl->cs, other.m_impl->cs);
59+
60+
if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
61+
m_impl->nTried != other.m_impl->nTried) {
62+
return false;
63+
}
64+
65+
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
66+
// Keys may be different.
67+
68+
auto addrinfo_hasher = [](const AddrInfo& a) {
69+
CSipHasher hasher(0, 0);
70+
auto addr_key = a.GetKey();
71+
auto source_key = a.source.GetAddrBytes();
72+
hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success));
73+
hasher.Write(a.nAttempts);
74+
hasher.Write(a.nRefCount);
75+
hasher.Write(a.fInTried);
76+
hasher.Write(a.GetNetwork());
77+
hasher.Write(a.source.GetNetwork());
78+
hasher.Write(addr_key.size());
79+
hasher.Write(source_key.size());
80+
hasher.Write(addr_key);
81+
hasher.Write(source_key);
82+
return (size_t)hasher.Finalize();
83+
};
84+
85+
auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
86+
return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
87+
std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
88+
};
89+
90+
using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
91+
92+
const size_t num_addresses{m_impl->mapInfo.size()};
93+
94+
Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
95+
for (const auto& [id, addr] : m_impl->mapInfo) {
96+
addresses.insert(addr);
97+
}
98+
99+
Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
100+
for (const auto& [id, addr] : other.m_impl->mapInfo) {
101+
other_addresses.insert(addr);
102+
}
103+
104+
if (addresses != other_addresses) {
105+
return false;
106+
}
107+
108+
auto IdsReferToSameAddress = [&](nid_type id, nid_type other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
109+
if (id == -1 && other_id == -1) {
110+
return true;
111+
}
112+
if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
113+
return false;
114+
}
115+
return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
116+
};
117+
118+
// Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
119+
// contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
120+
// themselves may differ between `vvNew` and `other.vvNew`.
121+
for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
122+
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
123+
if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
124+
return false;
125+
}
126+
}
127+
}
128+
129+
// Same for `vvTried`.
130+
for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
131+
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
132+
if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
133+
return false;
134+
}
135+
}
136+
}
137+
138+
return true;
139+
}
140+
};
141+
37142
class FuzzedSock : public Sock
38143
{
39144
FuzzedDataProvider& m_fuzzed_data_provider;
@@ -93,6 +198,13 @@ class FuzzedSock : public Sock
93198
return FuzzedSock{fuzzed_data_provider};
94199
}
95200

201+
[[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept
202+
{
203+
std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
204+
if (!SanityCheckASMap(asmap, 128)) asmap.clear();
205+
return NetGroupManager(asmap);
206+
}
207+
96208
inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
97209
{
98210
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};

0 commit comments

Comments
 (0)