Skip to content

Commit f41f972

Browse files
committed
Merge bitcoin/bitcoin#28584: Fuzz: extend CConnman tests
0802398 fuzz: make it possible to mock (fuzz) CThreadInterrupt (Vasil Dimov) 6d9e5d1 fuzz: add CConnman::SocketHandler() to the tests (Vasil Dimov) 3265df6 fuzz: add CConnman::InitBinds() to the tests (Vasil Dimov) 91cbf4d fuzz: add CConnman::CreateNodeFromAcceptedSocket() to the tests (Vasil Dimov) 50da743 fuzz: add CConnman::OpenNetworkConnection() to the tests (Vasil Dimov) e6a917c fuzz: add Fuzzed NetEventsInterface and use it in connman tests (Vasil Dimov) e883b37 fuzz: set the output argument of FuzzedSock::Accept() (Vasil Dimov) Pull request description: Extend `CConnman` fuzz tests to also exercise the methods `OpenNetworkConnection()`, `CreateNodeFromAcceptedSocket()`, `InitBinds()` and `SocketHandler()`. Previously fuzzing those methods would have resulted in real socket functions being called in the operating system which is undesirable during fuzzing. Now that bitcoin/bitcoin#21878 is complete all those are mocked to a fuzzed socket and a fuzzed DNS resolver (see how `CreateSock` and `g_dns_lookup` are replaced in the first commit). ACKs for top commit: achow101: ACK 0802398 jonatack: Review re-ACK 0802398 dergoegge: Code review ACK 0802398 Tree-SHA512: a717d4e79f42bacf2b029c821fdc265e10e4e5c41af77cd4cb452cc5720ec83c62789d5b3dfafd39a22cc8c0500b18169aa7864d497dded729a32ab863dd6c4d
2 parents cc4a2cc + 0802398 commit f41f972

File tree

15 files changed

+303
-65
lines changed

15 files changed

+303
-65
lines changed

src/i2p.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ namespace sam {
119119

120120
Session::Session(const fs::path& private_key_file,
121121
const Proxy& control_host,
122-
CThreadInterrupt* interrupt)
122+
std::shared_ptr<CThreadInterrupt> interrupt)
123123
: m_private_key_file{private_key_file},
124124
m_control_host{control_host},
125125
m_interrupt{interrupt},
126126
m_transient{false}
127127
{
128128
}
129129

130-
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
130+
Session::Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt)
131131
: m_control_host{control_host},
132132
m_interrupt{interrupt},
133133
m_transient{true}
@@ -162,7 +162,7 @@ bool Session::Accept(Connection& conn)
162162
std::string errmsg;
163163
bool disconnect{false};
164164

165-
while (!*m_interrupt) {
165+
while (!m_interrupt->interrupted()) {
166166
Sock::Event occurred;
167167
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
168168
errmsg = "wait on socket failed";
@@ -205,7 +205,7 @@ bool Session::Accept(Connection& conn)
205205
return true;
206206
}
207207

208-
if (*m_interrupt) {
208+
if (m_interrupt->interrupted()) {
209209
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
210210
} else {
211211
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);

src/i2p.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,11 @@ class Session
6363
* private key will be generated and saved into the file.
6464
* @param[in] control_host Location of the SAM proxy.
6565
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
66-
* possible and executing methods throw an exception. Notice: only a pointer to the
67-
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
68-
* `Session` object.
66+
* possible and executing methods throw an exception.
6967
*/
7068
Session(const fs::path& private_key_file,
7169
const Proxy& control_host,
72-
CThreadInterrupt* interrupt);
70+
std::shared_ptr<CThreadInterrupt> interrupt);
7371

7472
/**
7573
* Construct a transient session which will generate its own I2P private key
@@ -78,11 +76,9 @@ class Session
7876
* the session will be lazily created later when first used.
7977
* @param[in] control_host Location of the SAM proxy.
8078
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
81-
* possible and executing methods throw an exception. Notice: only a pointer to the
82-
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
83-
* `Session` object.
79+
* possible and executing methods throw an exception.
8480
*/
85-
Session(const Proxy& control_host, CThreadInterrupt* interrupt);
81+
Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt);
8682

8783
/**
8884
* Destroy the session, closing the internally used sockets. The sockets that have been
@@ -235,7 +231,7 @@ class Session
235231
/**
236232
* Cease network activity when this is signaled.
237233
*/
238-
CThreadInterrupt* const m_interrupt;
234+
const std::shared_ptr<CThreadInterrupt> m_interrupt;
239235

240236
/**
241237
* Mutex protecting the members that can be concurrently accessed.

src/net.cpp

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
477477
LOCK(m_unused_i2p_sessions_mutex);
478478
if (m_unused_i2p_sessions.empty()) {
479479
i2p_transient_session =
480-
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
480+
std::make_unique<i2p::sam::Session>(proxy, m_interrupt_net);
481481
} else {
482482
i2p_transient_session.swap(m_unused_i2p_sessions.front());
483483
m_unused_i2p_sessions.pop();
@@ -2103,7 +2103,7 @@ void CConnman::SocketHandler()
21032103
// empty sets.
21042104
events_per_sock = GenerateWaitSockets(snap.Nodes());
21052105
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
2106-
interruptNet.sleep_for(timeout);
2106+
m_interrupt_net->sleep_for(timeout);
21072107
}
21082108

21092109
// Service (send/receive) each of the already connected nodes.
@@ -2120,8 +2120,9 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
21202120
AssertLockNotHeld(m_total_bytes_sent_mutex);
21212121

21222122
for (CNode* pnode : nodes) {
2123-
if (interruptNet)
2123+
if (m_interrupt_net->interrupted()) {
21242124
return;
2125+
}
21252126

21262127
//
21272128
// Receive
@@ -2216,7 +2217,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
22162217
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
22172218
{
22182219
for (const ListenSocket& listen_socket : vhListenSocket) {
2219-
if (interruptNet) {
2220+
if (m_interrupt_net->interrupted()) {
22202221
return;
22212222
}
22222223
const auto it = events_per_sock.find(listen_socket.sock);
@@ -2230,8 +2231,7 @@ void CConnman::ThreadSocketHandler()
22302231
{
22312232
AssertLockNotHeld(m_total_bytes_sent_mutex);
22322233

2233-
while (!interruptNet)
2234-
{
2234+
while (!m_interrupt_net->interrupted()) {
22352235
DisconnectNodes();
22362236
NotifyNumConnectionsChanged();
22372237
SocketHandler();
@@ -2255,9 +2255,10 @@ void CConnman::ThreadDNSAddressSeed()
22552255
auto start = NodeClock::now();
22562256
constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
22572257
LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
2258-
while (!interruptNet) {
2259-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2258+
while (!m_interrupt_net->interrupted()) {
2259+
if (!m_interrupt_net->sleep_for(500ms)) {
22602260
return;
2261+
}
22612262

22622263
// Abort if we have spent enough time without reaching our target.
22632264
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
@@ -2318,7 +2319,7 @@ void CConnman::ThreadDNSAddressSeed()
23182319
// early to see if we have enough peers and can stop
23192320
// this thread entirely freeing up its resources
23202321
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
2321-
if (!interruptNet.sleep_for(w)) return;
2322+
if (!m_interrupt_net->sleep_for(w)) return;
23222323
to_wait -= w;
23232324

23242325
if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
@@ -2334,13 +2335,13 @@ void CConnman::ThreadDNSAddressSeed()
23342335
}
23352336
}
23362337

2337-
if (interruptNet) return;
2338+
if (m_interrupt_net->interrupted()) return;
23382339

23392340
// hold off on querying seeds if P2P network deactivated
23402341
if (!fNetworkActive) {
23412342
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
23422343
do {
2343-
if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
2344+
if (!m_interrupt_net->sleep_for(1s)) return;
23442345
} while (!fNetworkActive);
23452346
}
23462347

@@ -2535,12 +2536,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
25352536
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
25362537
for (int i = 0; i < 10 && i < nLoop; i++)
25372538
{
2538-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2539+
if (!m_interrupt_net->sleep_for(500ms)) {
25392540
return;
2541+
}
25402542
}
25412543
}
2542-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2544+
if (!m_interrupt_net->sleep_for(500ms)) {
25432545
return;
2546+
}
25442547
PerformReconnections();
25452548
}
25462549
}
@@ -2564,8 +2567,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
25642567
LogPrintf("Fixed seeds are disabled\n");
25652568
}
25662569

2567-
while (!interruptNet)
2568-
{
2570+
while (!m_interrupt_net->interrupted()) {
25692571
if (add_addr_fetch) {
25702572
add_addr_fetch = false;
25712573
const auto& seed{SpanPopBack(seed_nodes)};
@@ -2580,14 +2582,16 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
25802582

25812583
ProcessAddrFetch();
25822584

2583-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2585+
if (!m_interrupt_net->sleep_for(500ms)) {
25842586
return;
2587+
}
25852588

25862589
PerformReconnections();
25872590

25882591
CountingSemaphoreGrant<> grant(*semOutbound);
2589-
if (interruptNet)
2592+
if (m_interrupt_net->interrupted()) {
25902593
return;
2594+
}
25912595

25922596
const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
25932597
if (add_fixed_seeds && !fixed_seed_networks.empty()) {
@@ -2761,8 +2765,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
27612765
int nTries = 0;
27622766
const auto reachable_nets{g_reachable_nets.All()};
27632767

2764-
while (!interruptNet)
2765-
{
2768+
while (!m_interrupt_net->interrupted()) {
27662769
if (anchor && !m_anchors.empty()) {
27672770
const CAddress addr = m_anchors.back();
27682771
m_anchors.pop_back();
@@ -2864,7 +2867,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
28642867
if (addrConnect.IsValid()) {
28652868
if (fFeeler) {
28662869
// Add small amount of random noise before connection to avoid synchronization.
2867-
if (!interruptNet.sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
2870+
if (!m_interrupt_net->sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
28682871
return;
28692872
}
28702873
LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
@@ -2975,14 +2978,15 @@ void CConnman::ThreadOpenAddedConnections()
29752978
tried = true;
29762979
CAddress addr(CService(), NODE_NONE);
29772980
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
2978-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
2981+
if (!m_interrupt_net->sleep_for(500ms)) return;
29792982
grant = CountingSemaphoreGrant<>(*semAddnode, /*fTry=*/true);
29802983
}
29812984
// See if any reconnections are desired.
29822985
PerformReconnections();
29832986
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
2984-
if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)))
2987+
if (!m_interrupt_net->sleep_for(tried ? 60s : 2s)) {
29852988
return;
2989+
}
29862990
}
29872991
}
29882992

@@ -2995,7 +2999,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
29952999
//
29963000
// Initiate outbound network connection
29973001
//
2998-
if (interruptNet) {
3002+
if (m_interrupt_net->interrupted()) {
29993003
return;
30003004
}
30013005
if (!fNetworkActive) {
@@ -3083,13 +3087,13 @@ void CConnman::ThreadI2PAcceptIncoming()
30833087
i2p::Connection conn;
30843088

30853089
auto SleepOnFailure = [&]() {
3086-
interruptNet.sleep_for(err_wait);
3090+
m_interrupt_net->sleep_for(err_wait);
30873091
if (err_wait < err_wait_cap) {
30883092
err_wait += 1s;
30893093
}
30903094
};
30913095

3092-
while (!interruptNet) {
3096+
while (!m_interrupt_net->interrupted()) {
30933097

30943098
if (!m_i2p_sam_session->Listen(conn)) {
30953099
if (advertising_listen_addr && conn.me.IsValid()) {
@@ -3211,12 +3215,18 @@ void CConnman::SetNetworkActive(bool active)
32113215
}
32123216
}
32133217

3214-
CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in,
3215-
const NetGroupManager& netgroupman, const CChainParams& params, bool network_active)
3218+
CConnman::CConnman(uint64_t nSeed0In,
3219+
uint64_t nSeed1In,
3220+
AddrMan& addrman_in,
3221+
const NetGroupManager& netgroupman,
3222+
const CChainParams& params,
3223+
bool network_active,
3224+
std::shared_ptr<CThreadInterrupt> interrupt_net)
32163225
: addrman(addrman_in)
32173226
, m_netgroupman{netgroupman}
32183227
, nSeed0(nSeed0In)
32193228
, nSeed1(nSeed1In)
3229+
, m_interrupt_net{interrupt_net}
32203230
, m_params(params)
32213231
{
32223232
SetTryNewOutboundPeer(false);
@@ -3312,7 +3322,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
33123322
Proxy i2p_sam;
33133323
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
33143324
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
3315-
i2p_sam, &interruptNet);
3325+
i2p_sam, m_interrupt_net);
33163326
}
33173327

33183328
// Randomize the order in which we may query seednode to potentially prevent connecting to the same one every restart (and signal that we have restarted)
@@ -3349,7 +3359,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
33493359
// Start threads
33503360
//
33513361
assert(m_msgproc);
3352-
interruptNet.reset();
3362+
m_interrupt_net->reset();
33533363
flagInterruptMsgProc = false;
33543364

33553365
{
@@ -3425,7 +3435,7 @@ void CConnman::Interrupt()
34253435
}
34263436
condMsgProc.notify_all();
34273437

3428-
interruptNet();
3438+
(*m_interrupt_net)();
34293439
g_socks5_interrupt();
34303440

34313441
if (semOutbound) {

src/net.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,8 +1119,13 @@ class CConnman
11191119
whitelist_relay = connOptions.whitelist_relay;
11201120
}
11211121

1122-
CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, const NetGroupManager& netgroupman,
1123-
const CChainParams& params, bool network_active = true);
1122+
CConnman(uint64_t seed0,
1123+
uint64_t seed1,
1124+
AddrMan& addrman,
1125+
const NetGroupManager& netgroupman,
1126+
const CChainParams& params,
1127+
bool network_active = true,
1128+
std::shared_ptr<CThreadInterrupt> interrupt_net = std::make_shared<CThreadInterrupt>());
11241129

11251130
~CConnman();
11261131

@@ -1555,11 +1560,9 @@ class CConnman
15551560

15561561
/**
15571562
* This is signaled when network activity should cease.
1558-
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
1559-
* the lifetime of `interruptNet` is not shorter than
1560-
* the lifetime of `m_i2p_sam_session`.
1563+
* A copy of this is saved in `m_i2p_sam_session`.
15611564
*/
1562-
CThreadInterrupt interruptNet;
1565+
const std::shared_ptr<CThreadInterrupt> m_interrupt_net;
15631566

15641567
/**
15651568
* I2P SAM session.

0 commit comments

Comments
 (0)