Skip to content

Commit 0802398

Browse files
committed
fuzz: make it possible to mock (fuzz) CThreadInterrupt
* Make the methods of `CThreadInterrupt` virtual and store a pointer to it in `CConnman`, thus making it possible to override with a mocked instance. * Initialize `CConnman::m_interrupt_net` from the constructor, making it possible for callers to supply mocked version. * Introduce `FuzzedThreadInterrupt` and `ConsumeThreadInterrupt()` and use them in `src/test/fuzz/connman.cpp` and `src/test/fuzz/i2p.cpp`. This improves the CPU utilization of the `connman` fuzz test. As a nice side effect, the `std::shared_ptr` used for `CConnman::m_interrupt_net` resolves the possible lifetime issues with it (see the removed comment for that variable).
1 parent 6d9e5d1 commit 0802398

File tree

12 files changed

+154
-65
lines changed

12 files changed

+154
-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
@@ -471,7 +471,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
471471
LOCK(m_unused_i2p_sessions_mutex);
472472
if (m_unused_i2p_sessions.empty()) {
473473
i2p_transient_session =
474-
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
474+
std::make_unique<i2p::sam::Session>(proxy, m_interrupt_net);
475475
} else {
476476
i2p_transient_session.swap(m_unused_i2p_sessions.front());
477477
m_unused_i2p_sessions.pop();
@@ -2094,7 +2094,7 @@ void CConnman::SocketHandler()
20942094
// empty sets.
20952095
events_per_sock = GenerateWaitSockets(snap.Nodes());
20962096
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
2097-
interruptNet.sleep_for(timeout);
2097+
m_interrupt_net->sleep_for(timeout);
20982098
}
20992099

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

21132113
for (CNode* pnode : nodes) {
2114-
if (interruptNet)
2114+
if (m_interrupt_net->interrupted()) {
21152115
return;
2116+
}
21162117

21172118
//
21182119
// Receive
@@ -2207,7 +2208,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
22072208
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
22082209
{
22092210
for (const ListenSocket& listen_socket : vhListenSocket) {
2210-
if (interruptNet) {
2211+
if (m_interrupt_net->interrupted()) {
22112212
return;
22122213
}
22132214
const auto it = events_per_sock.find(listen_socket.sock);
@@ -2221,8 +2222,7 @@ void CConnman::ThreadSocketHandler()
22212222
{
22222223
AssertLockNotHeld(m_total_bytes_sent_mutex);
22232224

2224-
while (!interruptNet)
2225-
{
2225+
while (!m_interrupt_net->interrupted()) {
22262226
DisconnectNodes();
22272227
NotifyNumConnectionsChanged();
22282228
SocketHandler();
@@ -2246,9 +2246,10 @@ void CConnman::ThreadDNSAddressSeed()
22462246
auto start = NodeClock::now();
22472247
constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
22482248
LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
2249-
while (!interruptNet) {
2250-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2249+
while (!m_interrupt_net->interrupted()) {
2250+
if (!m_interrupt_net->sleep_for(500ms)) {
22512251
return;
2252+
}
22522253

22532254
// Abort if we have spent enough time without reaching our target.
22542255
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
@@ -2309,7 +2310,7 @@ void CConnman::ThreadDNSAddressSeed()
23092310
// early to see if we have enough peers and can stop
23102311
// this thread entirely freeing up its resources
23112312
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
2312-
if (!interruptNet.sleep_for(w)) return;
2313+
if (!m_interrupt_net->sleep_for(w)) return;
23132314
to_wait -= w;
23142315

23152316
if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
@@ -2325,13 +2326,13 @@ void CConnman::ThreadDNSAddressSeed()
23252326
}
23262327
}
23272328

2328-
if (interruptNet) return;
2329+
if (m_interrupt_net->interrupted()) return;
23292330

23302331
// hold off on querying seeds if P2P network deactivated
23312332
if (!fNetworkActive) {
23322333
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
23332334
do {
2334-
if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
2335+
if (!m_interrupt_net->sleep_for(1s)) return;
23352336
} while (!fNetworkActive);
23362337
}
23372338

@@ -2526,12 +2527,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
25262527
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
25272528
for (int i = 0; i < 10 && i < nLoop; i++)
25282529
{
2529-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2530+
if (!m_interrupt_net->sleep_for(500ms)) {
25302531
return;
2532+
}
25312533
}
25322534
}
2533-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2535+
if (!m_interrupt_net->sleep_for(500ms)) {
25342536
return;
2537+
}
25352538
PerformReconnections();
25362539
}
25372540
}
@@ -2555,8 +2558,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
25552558
LogPrintf("Fixed seeds are disabled\n");
25562559
}
25572560

2558-
while (!interruptNet)
2559-
{
2561+
while (!m_interrupt_net->interrupted()) {
25602562
if (add_addr_fetch) {
25612563
add_addr_fetch = false;
25622564
const auto& seed{SpanPopBack(seed_nodes)};
@@ -2571,14 +2573,16 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
25712573

25722574
ProcessAddrFetch();
25732575

2574-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
2576+
if (!m_interrupt_net->sleep_for(500ms)) {
25752577
return;
2578+
}
25762579

25772580
PerformReconnections();
25782581

25792582
CountingSemaphoreGrant<> grant(*semOutbound);
2580-
if (interruptNet)
2583+
if (m_interrupt_net->interrupted()) {
25812584
return;
2585+
}
25822586

25832587
const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
25842588
if (add_fixed_seeds && !fixed_seed_networks.empty()) {
@@ -2752,8 +2756,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
27522756
int nTries = 0;
27532757
const auto reachable_nets{g_reachable_nets.All()};
27542758

2755-
while (!interruptNet)
2756-
{
2759+
while (!m_interrupt_net->interrupted()) {
27572760
if (anchor && !m_anchors.empty()) {
27582761
const CAddress addr = m_anchors.back();
27592762
m_anchors.pop_back();
@@ -2855,7 +2858,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
28552858
if (addrConnect.IsValid()) {
28562859
if (fFeeler) {
28572860
// Add small amount of random noise before connection to avoid synchronization.
2858-
if (!interruptNet.sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
2861+
if (!m_interrupt_net->sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
28592862
return;
28602863
}
28612864
LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
@@ -2966,14 +2969,15 @@ void CConnman::ThreadOpenAddedConnections()
29662969
tried = true;
29672970
CAddress addr(CService(), NODE_NONE);
29682971
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
2969-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
2972+
if (!m_interrupt_net->sleep_for(500ms)) return;
29702973
grant = CountingSemaphoreGrant<>(*semAddnode, /*fTry=*/true);
29712974
}
29722975
// See if any reconnections are desired.
29732976
PerformReconnections();
29742977
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
2975-
if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)))
2978+
if (!m_interrupt_net->sleep_for(tried ? 60s : 2s)) {
29762979
return;
2980+
}
29772981
}
29782982
}
29792983

@@ -2986,7 +2990,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
29862990
//
29872991
// Initiate outbound network connection
29882992
//
2989-
if (interruptNet) {
2993+
if (m_interrupt_net->interrupted()) {
29902994
return;
29912995
}
29922996
if (!fNetworkActive) {
@@ -3074,13 +3078,13 @@ void CConnman::ThreadI2PAcceptIncoming()
30743078
i2p::Connection conn;
30753079

30763080
auto SleepOnFailure = [&]() {
3077-
interruptNet.sleep_for(err_wait);
3081+
m_interrupt_net->sleep_for(err_wait);
30783082
if (err_wait < err_wait_cap) {
30793083
err_wait += 1s;
30803084
}
30813085
};
30823086

3083-
while (!interruptNet) {
3087+
while (!m_interrupt_net->interrupted()) {
30843088

30853089
if (!m_i2p_sam_session->Listen(conn)) {
30863090
if (advertising_listen_addr && conn.me.IsValid()) {
@@ -3202,12 +3206,18 @@ void CConnman::SetNetworkActive(bool active)
32023206
}
32033207
}
32043208

3205-
CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in,
3206-
const NetGroupManager& netgroupman, const CChainParams& params, bool network_active)
3209+
CConnman::CConnman(uint64_t nSeed0In,
3210+
uint64_t nSeed1In,
3211+
AddrMan& addrman_in,
3212+
const NetGroupManager& netgroupman,
3213+
const CChainParams& params,
3214+
bool network_active,
3215+
std::shared_ptr<CThreadInterrupt> interrupt_net)
32073216
: addrman(addrman_in)
32083217
, m_netgroupman{netgroupman}
32093218
, nSeed0(nSeed0In)
32103219
, nSeed1(nSeed1In)
3220+
, m_interrupt_net{interrupt_net}
32113221
, m_params(params)
32123222
{
32133223
SetTryNewOutboundPeer(false);
@@ -3303,7 +3313,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
33033313
Proxy i2p_sam;
33043314
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
33053315
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
3306-
i2p_sam, &interruptNet);
3316+
i2p_sam, m_interrupt_net);
33073317
}
33083318

33093319
// 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)
@@ -3340,7 +3350,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
33403350
// Start threads
33413351
//
33423352
assert(m_msgproc);
3343-
interruptNet.reset();
3353+
m_interrupt_net->reset();
33443354
flagInterruptMsgProc = false;
33453355

33463356
{
@@ -3416,7 +3426,7 @@ void CConnman::Interrupt()
34163426
}
34173427
condMsgProc.notify_all();
34183428

3419-
interruptNet();
3429+
(*m_interrupt_net)();
34203430
g_socks5_interrupt();
34213431

34223432
if (semOutbound) {

src/net.h

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

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

11241129
~CConnman();
11251130

@@ -1543,11 +1548,9 @@ class CConnman
15431548

15441549
/**
15451550
* This is signaled when network activity should cease.
1546-
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
1547-
* the lifetime of `interruptNet` is not shorter than
1548-
* the lifetime of `m_i2p_sam_session`.
1551+
* A copy of this is saved in `m_i2p_sam_session`.
15491552
*/
1550-
CThreadInterrupt interruptNet;
1553+
const std::shared_ptr<CThreadInterrupt> m_interrupt_net;
15511554

15521555
/**
15531556
* I2P SAM session.

src/test/fuzz/connman.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <test/fuzz/fuzz.h>
1414
#include <test/fuzz/util.h>
1515
#include <test/fuzz/util/net.h>
16+
#include <test/fuzz/util/threadinterrupt.h>
1617
#include <test/util/setup_common.h>
1718
#include <util/translation.h>
1819

@@ -71,7 +72,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
7172
addr_man,
7273
netgroupman,
7374
Params(),
74-
fuzzed_data_provider.ConsumeBool()};
75+
fuzzed_data_provider.ConsumeBool(),
76+
ConsumeThreadInterrupt(fuzzed_data_provider)};
7577

7678
const uint64_t max_outbound_limit{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
7779
CConnman::Options options;

0 commit comments

Comments
 (0)