mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 03:47:29 -03:00
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).
This commit is contained in:
parent
4deb5e2121
commit
33d6a80c92
12 changed files with 154 additions and 65 deletions
|
@ -118,7 +118,7 @@ namespace sam {
|
||||||
|
|
||||||
Session::Session(const fs::path& private_key_file,
|
Session::Session(const fs::path& private_key_file,
|
||||||
const Proxy& control_host,
|
const Proxy& control_host,
|
||||||
CThreadInterrupt* interrupt)
|
std::shared_ptr<CThreadInterrupt> interrupt)
|
||||||
: m_private_key_file{private_key_file},
|
: m_private_key_file{private_key_file},
|
||||||
m_control_host{control_host},
|
m_control_host{control_host},
|
||||||
m_interrupt{interrupt},
|
m_interrupt{interrupt},
|
||||||
|
@ -126,7 +126,7 @@ Session::Session(const fs::path& private_key_file,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
|
Session::Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt)
|
||||||
: m_control_host{control_host},
|
: m_control_host{control_host},
|
||||||
m_interrupt{interrupt},
|
m_interrupt{interrupt},
|
||||||
m_transient{true}
|
m_transient{true}
|
||||||
|
@ -161,7 +161,7 @@ bool Session::Accept(Connection& conn)
|
||||||
std::string errmsg;
|
std::string errmsg;
|
||||||
bool disconnect{false};
|
bool disconnect{false};
|
||||||
|
|
||||||
while (!*m_interrupt) {
|
while (!m_interrupt->interrupted()) {
|
||||||
Sock::Event occurred;
|
Sock::Event occurred;
|
||||||
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
|
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
|
||||||
errmsg = "wait on socket failed";
|
errmsg = "wait on socket failed";
|
||||||
|
@ -204,7 +204,7 @@ bool Session::Accept(Connection& conn)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*m_interrupt) {
|
if (m_interrupt->interrupted()) {
|
||||||
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
|
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
|
||||||
} else {
|
} else {
|
||||||
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);
|
LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);
|
||||||
|
|
14
src/i2p.h
14
src/i2p.h
|
@ -63,13 +63,11 @@ public:
|
||||||
* private key will be generated and saved into the file.
|
* private key will be generated and saved into the file.
|
||||||
* @param[in] control_host Location of the SAM proxy.
|
* @param[in] control_host Location of the SAM proxy.
|
||||||
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
||||||
* possible and executing methods throw an exception. Notice: only a pointer to the
|
* possible and executing methods throw an exception.
|
||||||
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
|
|
||||||
* `Session` object.
|
|
||||||
*/
|
*/
|
||||||
Session(const fs::path& private_key_file,
|
Session(const fs::path& private_key_file,
|
||||||
const Proxy& control_host,
|
const Proxy& control_host,
|
||||||
CThreadInterrupt* interrupt);
|
std::shared_ptr<CThreadInterrupt> interrupt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a transient session which will generate its own I2P private key
|
* Construct a transient session which will generate its own I2P private key
|
||||||
|
@ -78,11 +76,9 @@ public:
|
||||||
* the session will be lazily created later when first used.
|
* the session will be lazily created later when first used.
|
||||||
* @param[in] control_host Location of the SAM proxy.
|
* @param[in] control_host Location of the SAM proxy.
|
||||||
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
* @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
|
||||||
* possible and executing methods throw an exception. Notice: only a pointer to the
|
* possible and executing methods throw an exception.
|
||||||
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
|
|
||||||
* `Session` object.
|
|
||||||
*/
|
*/
|
||||||
Session(const Proxy& control_host, CThreadInterrupt* interrupt);
|
Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the session, closing the internally used sockets. The sockets that have been
|
* Destroy the session, closing the internally used sockets. The sockets that have been
|
||||||
|
@ -235,7 +231,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* Cease network activity when this is signaled.
|
* Cease network activity when this is signaled.
|
||||||
*/
|
*/
|
||||||
CThreadInterrupt* const m_interrupt;
|
const std::shared_ptr<CThreadInterrupt> m_interrupt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutex protecting the members that can be concurrently accessed.
|
* Mutex protecting the members that can be concurrently accessed.
|
||||||
|
|
70
src/net.cpp
70
src/net.cpp
|
@ -474,7 +474,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||||
LOCK(m_unused_i2p_sessions_mutex);
|
LOCK(m_unused_i2p_sessions_mutex);
|
||||||
if (m_unused_i2p_sessions.empty()) {
|
if (m_unused_i2p_sessions.empty()) {
|
||||||
i2p_transient_session =
|
i2p_transient_session =
|
||||||
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
|
std::make_unique<i2p::sam::Session>(proxy, m_interrupt_net);
|
||||||
} else {
|
} else {
|
||||||
i2p_transient_session.swap(m_unused_i2p_sessions.front());
|
i2p_transient_session.swap(m_unused_i2p_sessions.front());
|
||||||
m_unused_i2p_sessions.pop();
|
m_unused_i2p_sessions.pop();
|
||||||
|
@ -2046,7 +2046,7 @@ void CConnman::SocketHandler()
|
||||||
// empty sets.
|
// empty sets.
|
||||||
events_per_sock = GenerateWaitSockets(snap.Nodes());
|
events_per_sock = GenerateWaitSockets(snap.Nodes());
|
||||||
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
|
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
|
||||||
interruptNet.sleep_for(timeout);
|
m_interrupt_net->sleep_for(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service (send/receive) each of the already connected nodes.
|
// Service (send/receive) each of the already connected nodes.
|
||||||
|
@ -2063,8 +2063,9 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
||||||
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
||||||
|
|
||||||
for (CNode* pnode : nodes) {
|
for (CNode* pnode : nodes) {
|
||||||
if (interruptNet)
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Receive
|
// Receive
|
||||||
|
@ -2155,7 +2156,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
||||||
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
|
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
|
||||||
{
|
{
|
||||||
for (const ListenSocket& listen_socket : vhListenSocket) {
|
for (const ListenSocket& listen_socket : vhListenSocket) {
|
||||||
if (interruptNet) {
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto it = events_per_sock.find(listen_socket.sock);
|
const auto it = events_per_sock.find(listen_socket.sock);
|
||||||
|
@ -2169,8 +2170,7 @@ void CConnman::ThreadSocketHandler()
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
||||||
|
|
||||||
while (!interruptNet)
|
while (!m_interrupt_net->interrupted()) {
|
||||||
{
|
|
||||||
DisconnectNodes();
|
DisconnectNodes();
|
||||||
NotifyNumConnectionsChanged();
|
NotifyNumConnectionsChanged();
|
||||||
SocketHandler();
|
SocketHandler();
|
||||||
|
@ -2194,9 +2194,10 @@ void CConnman::ThreadDNSAddressSeed()
|
||||||
auto start = NodeClock::now();
|
auto start = NodeClock::now();
|
||||||
constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
|
constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
|
||||||
LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
|
LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
|
||||||
while (!interruptNet) {
|
while (!m_interrupt_net->interrupted()) {
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Abort if we have spent enough time without reaching our target.
|
// Abort if we have spent enough time without reaching our target.
|
||||||
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
|
// Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
|
||||||
|
@ -2257,7 +2258,7 @@ void CConnman::ThreadDNSAddressSeed()
|
||||||
// early to see if we have enough peers and can stop
|
// early to see if we have enough peers and can stop
|
||||||
// this thread entirely freeing up its resources
|
// this thread entirely freeing up its resources
|
||||||
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
|
std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
|
||||||
if (!interruptNet.sleep_for(w)) return;
|
if (!m_interrupt_net->sleep_for(w)) return;
|
||||||
to_wait -= w;
|
to_wait -= w;
|
||||||
|
|
||||||
if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
|
if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
|
||||||
|
@ -2273,13 +2274,13 @@ void CConnman::ThreadDNSAddressSeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interruptNet) return;
|
if (m_interrupt_net->interrupted()) return;
|
||||||
|
|
||||||
// hold off on querying seeds if P2P network deactivated
|
// hold off on querying seeds if P2P network deactivated
|
||||||
if (!fNetworkActive) {
|
if (!fNetworkActive) {
|
||||||
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
|
LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
|
||||||
do {
|
do {
|
||||||
if (!interruptNet.sleep_for(std::chrono::seconds{1})) return;
|
if (!m_interrupt_net->sleep_for(1s)) return;
|
||||||
} while (!fNetworkActive);
|
} while (!fNetworkActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2474,12 +2475,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
|
||||||
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
|
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
|
||||||
for (int i = 0; i < 10 && i < nLoop; i++)
|
for (int i = 0; i < 10 && i < nLoop; i++)
|
||||||
{
|
{
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
PerformReconnections();
|
PerformReconnections();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2503,8 +2506,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
|
||||||
LogPrintf("Fixed seeds are disabled\n");
|
LogPrintf("Fixed seeds are disabled\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!interruptNet)
|
while (!m_interrupt_net->interrupted()) {
|
||||||
{
|
|
||||||
if (add_addr_fetch) {
|
if (add_addr_fetch) {
|
||||||
add_addr_fetch = false;
|
add_addr_fetch = false;
|
||||||
const auto& seed{SpanPopBack(seed_nodes)};
|
const auto& seed{SpanPopBack(seed_nodes)};
|
||||||
|
@ -2519,14 +2521,16 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
|
||||||
|
|
||||||
ProcessAddrFetch();
|
ProcessAddrFetch();
|
||||||
|
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
|
if (!m_interrupt_net->sleep_for(500ms)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PerformReconnections();
|
PerformReconnections();
|
||||||
|
|
||||||
CSemaphoreGrant grant(*semOutbound);
|
CSemaphoreGrant grant(*semOutbound);
|
||||||
if (interruptNet)
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
|
const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
|
||||||
if (add_fixed_seeds && !fixed_seed_networks.empty()) {
|
if (add_fixed_seeds && !fixed_seed_networks.empty()) {
|
||||||
|
@ -2700,8 +2704,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
|
||||||
int nTries = 0;
|
int nTries = 0;
|
||||||
const auto reachable_nets{g_reachable_nets.All()};
|
const auto reachable_nets{g_reachable_nets.All()};
|
||||||
|
|
||||||
while (!interruptNet)
|
while (!m_interrupt_net->interrupted()) {
|
||||||
{
|
|
||||||
if (anchor && !m_anchors.empty()) {
|
if (anchor && !m_anchors.empty()) {
|
||||||
const CAddress addr = m_anchors.back();
|
const CAddress addr = m_anchors.back();
|
||||||
m_anchors.pop_back();
|
m_anchors.pop_back();
|
||||||
|
@ -2803,7 +2806,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
|
||||||
if (addrConnect.IsValid()) {
|
if (addrConnect.IsValid()) {
|
||||||
if (fFeeler) {
|
if (fFeeler) {
|
||||||
// Add small amount of random noise before connection to avoid synchronization.
|
// Add small amount of random noise before connection to avoid synchronization.
|
||||||
if (!interruptNet.sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
|
if (!m_interrupt_net->sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
|
LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
|
||||||
|
@ -2914,14 +2917,15 @@ void CConnman::ThreadOpenAddedConnections()
|
||||||
tried = true;
|
tried = true;
|
||||||
CAddress addr(CService(), NODE_NONE);
|
CAddress addr(CService(), NODE_NONE);
|
||||||
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
|
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
|
||||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
|
if (!m_interrupt_net->sleep_for(500ms)) return;
|
||||||
grant = CSemaphoreGrant(*semAddnode, /*fTry=*/true);
|
grant = CSemaphoreGrant(*semAddnode, /*fTry=*/true);
|
||||||
}
|
}
|
||||||
// See if any reconnections are desired.
|
// See if any reconnections are desired.
|
||||||
PerformReconnections();
|
PerformReconnections();
|
||||||
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
|
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
|
||||||
if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)))
|
if (!m_interrupt_net->sleep_for(tried ? 60s : 2s)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2934,7 +2938,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
||||||
//
|
//
|
||||||
// Initiate outbound network connection
|
// Initiate outbound network connection
|
||||||
//
|
//
|
||||||
if (interruptNet) {
|
if (m_interrupt_net->interrupted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!fNetworkActive) {
|
if (!fNetworkActive) {
|
||||||
|
@ -3015,13 +3019,13 @@ void CConnman::ThreadI2PAcceptIncoming()
|
||||||
i2p::Connection conn;
|
i2p::Connection conn;
|
||||||
|
|
||||||
auto SleepOnFailure = [&]() {
|
auto SleepOnFailure = [&]() {
|
||||||
interruptNet.sleep_for(err_wait);
|
m_interrupt_net->sleep_for(err_wait);
|
||||||
if (err_wait < err_wait_cap) {
|
if (err_wait < err_wait_cap) {
|
||||||
err_wait += 1s;
|
err_wait += 1s;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (!interruptNet) {
|
while (!m_interrupt_net->interrupted()) {
|
||||||
|
|
||||||
if (!m_i2p_sam_session->Listen(conn)) {
|
if (!m_i2p_sam_session->Listen(conn)) {
|
||||||
if (advertising_listen_addr && conn.me.IsValid()) {
|
if (advertising_listen_addr && conn.me.IsValid()) {
|
||||||
|
@ -3144,12 +3148,18 @@ void CConnman::SetNetworkActive(bool active)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in,
|
CConnman::CConnman(uint64_t nSeed0In,
|
||||||
const NetGroupManager& netgroupman, const CChainParams& params, bool network_active)
|
uint64_t nSeed1In,
|
||||||
|
AddrMan& addrman_in,
|
||||||
|
const NetGroupManager& netgroupman,
|
||||||
|
const CChainParams& params,
|
||||||
|
bool network_active,
|
||||||
|
std::shared_ptr<CThreadInterrupt> interrupt_net)
|
||||||
: addrman(addrman_in)
|
: addrman(addrman_in)
|
||||||
, m_netgroupman{netgroupman}
|
, m_netgroupman{netgroupman}
|
||||||
, nSeed0(nSeed0In)
|
, nSeed0(nSeed0In)
|
||||||
, nSeed1(nSeed1In)
|
, nSeed1(nSeed1In)
|
||||||
|
, m_interrupt_net{interrupt_net}
|
||||||
, m_params(params)
|
, m_params(params)
|
||||||
{
|
{
|
||||||
SetTryNewOutboundPeer(false);
|
SetTryNewOutboundPeer(false);
|
||||||
|
@ -3245,7 +3255,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||||
Proxy i2p_sam;
|
Proxy i2p_sam;
|
||||||
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
|
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
|
||||||
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
|
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
|
||||||
i2p_sam, &interruptNet);
|
i2p_sam, m_interrupt_net);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
// 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)
|
||||||
|
@ -3282,7 +3292,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||||
// Start threads
|
// Start threads
|
||||||
//
|
//
|
||||||
assert(m_msgproc);
|
assert(m_msgproc);
|
||||||
interruptNet.reset();
|
m_interrupt_net->reset();
|
||||||
flagInterruptMsgProc = false;
|
flagInterruptMsgProc = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -3358,7 +3368,7 @@ void CConnman::Interrupt()
|
||||||
}
|
}
|
||||||
condMsgProc.notify_all();
|
condMsgProc.notify_all();
|
||||||
|
|
||||||
interruptNet();
|
(*m_interrupt_net)();
|
||||||
g_socks5_interrupt();
|
g_socks5_interrupt();
|
||||||
|
|
||||||
if (semOutbound) {
|
if (semOutbound) {
|
||||||
|
|
15
src/net.h
15
src/net.h
|
@ -1101,8 +1101,13 @@ public:
|
||||||
whitelist_relay = connOptions.whitelist_relay;
|
whitelist_relay = connOptions.whitelist_relay;
|
||||||
}
|
}
|
||||||
|
|
||||||
CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, const NetGroupManager& netgroupman,
|
CConnman(uint64_t seed0,
|
||||||
const CChainParams& params, bool network_active = true);
|
uint64_t seed1,
|
||||||
|
AddrMan& addrman,
|
||||||
|
const NetGroupManager& netgroupman,
|
||||||
|
const CChainParams& params,
|
||||||
|
bool network_active = true,
|
||||||
|
std::shared_ptr<CThreadInterrupt> interrupt_net = std::make_shared<CThreadInterrupt>());
|
||||||
|
|
||||||
~CConnman();
|
~CConnman();
|
||||||
|
|
||||||
|
@ -1526,11 +1531,9 @@ private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is signaled when network activity should cease.
|
* This is signaled when network activity should cease.
|
||||||
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
|
* A copy of this is saved in `m_i2p_sam_session`.
|
||||||
* the lifetime of `interruptNet` is not shorter than
|
|
||||||
* the lifetime of `m_i2p_sam_session`.
|
|
||||||
*/
|
*/
|
||||||
CThreadInterrupt interruptNet;
|
const std::shared_ptr<CThreadInterrupt> m_interrupt_net;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* I2P SAM session.
|
* I2P SAM session.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <test/fuzz/fuzz.h>
|
#include <test/fuzz/fuzz.h>
|
||||||
#include <test/fuzz/util.h>
|
#include <test/fuzz/util.h>
|
||||||
#include <test/fuzz/util/net.h>
|
#include <test/fuzz/util/net.h>
|
||||||
|
#include <test/fuzz/util/threadinterrupt.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
|
|
||||||
|
@ -69,7 +70,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
||||||
addr_man,
|
addr_man,
|
||||||
netgroupman,
|
netgroupman,
|
||||||
Params(),
|
Params(),
|
||||||
fuzzed_data_provider.ConsumeBool()};
|
fuzzed_data_provider.ConsumeBool(),
|
||||||
|
ConsumeThreadInterrupt(fuzzed_data_provider)};
|
||||||
|
|
||||||
const uint64_t max_outbound_limit{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
|
const uint64_t max_outbound_limit{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
|
||||||
CConnman::Options options;
|
CConnman::Options options;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <test/fuzz/fuzz.h>
|
#include <test/fuzz/fuzz.h>
|
||||||
#include <test/fuzz/util.h>
|
#include <test/fuzz/util.h>
|
||||||
#include <test/fuzz/util/net.h>
|
#include <test/fuzz/util/net.h>
|
||||||
|
#include <test/fuzz/util/threadinterrupt.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <util/fs_helpers.h>
|
#include <util/fs_helpers.h>
|
||||||
#include <util/threadinterrupt.h>
|
#include <util/threadinterrupt.h>
|
||||||
|
@ -34,15 +35,15 @@ FUZZ_TARGET(i2p, .init = initialize_i2p)
|
||||||
const fs::path private_key_path = gArgs.GetDataDirNet() / "fuzzed_i2p_private_key";
|
const fs::path private_key_path = gArgs.GetDataDirNet() / "fuzzed_i2p_private_key";
|
||||||
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), 7656};
|
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), 7656};
|
||||||
const Proxy sam_proxy{addr, false};
|
const Proxy sam_proxy{addr, false};
|
||||||
CThreadInterrupt interrupt;
|
auto interrupt{ConsumeThreadInterrupt(fuzzed_data_provider)};
|
||||||
|
|
||||||
i2p::sam::Session session{private_key_path, sam_proxy, &interrupt};
|
i2p::sam::Session session{private_key_path, sam_proxy, interrupt};
|
||||||
i2p::Connection conn;
|
i2p::Connection conn;
|
||||||
|
|
||||||
if (session.Listen(conn)) {
|
if (session.Listen(conn)) {
|
||||||
if (session.Accept(conn)) {
|
if (session.Accept(conn)) {
|
||||||
try {
|
try {
|
||||||
(void)conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
|
(void)conn.sock->RecvUntilTerminator('\n', 10ms, *interrupt, i2p::sam::MAX_MSG_SIZE);
|
||||||
} catch (const std::runtime_error&) {
|
} catch (const std::runtime_error&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +53,7 @@ FUZZ_TARGET(i2p, .init = initialize_i2p)
|
||||||
|
|
||||||
if (session.Connect(CService{}, conn, proxy_error)) {
|
if (session.Connect(CService{}, conn, proxy_error)) {
|
||||||
try {
|
try {
|
||||||
conn.sock->SendComplete("verack\n", 10ms, interrupt);
|
conn.sock->SendComplete("verack\n", 10ms, *interrupt);
|
||||||
} catch (const std::runtime_error&) {
|
} catch (const std::runtime_error&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ add_library(test_fuzz STATIC EXCLUDE_FROM_ALL
|
||||||
descriptor.cpp
|
descriptor.cpp
|
||||||
mempool.cpp
|
mempool.cpp
|
||||||
net.cpp
|
net.cpp
|
||||||
|
threadinterrupt.cpp
|
||||||
../fuzz.cpp
|
../fuzz.cpp
|
||||||
../util.cpp
|
../util.cpp
|
||||||
)
|
)
|
||||||
|
|
22
src/test/fuzz/util/threadinterrupt.cpp
Normal file
22
src/test/fuzz/util/threadinterrupt.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) 2024-present The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <test/fuzz/util.h>
|
||||||
|
#include <test/fuzz/util/threadinterrupt.h>
|
||||||
|
|
||||||
|
FuzzedThreadInterrupt::FuzzedThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider)
|
||||||
|
: m_fuzzed_data_provider{fuzzed_data_provider}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FuzzedThreadInterrupt::interrupted() const
|
||||||
|
{
|
||||||
|
return m_fuzzed_data_provider.ConsumeBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FuzzedThreadInterrupt::sleep_for(Clock::duration)
|
||||||
|
{
|
||||||
|
SetMockTime(ConsumeTime(m_fuzzed_data_provider)); // Time could go backwards.
|
||||||
|
return m_fuzzed_data_provider.ConsumeBool();
|
||||||
|
}
|
33
src/test/fuzz/util/threadinterrupt.h
Normal file
33
src/test/fuzz/util/threadinterrupt.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright (c) 2024-present The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_TEST_FUZZ_UTIL_THREADINTERRUPT_H
|
||||||
|
#define BITCOIN_TEST_FUZZ_UTIL_THREADINTERRUPT_H
|
||||||
|
|
||||||
|
#include <test/fuzz/FuzzedDataProvider.h>
|
||||||
|
#include <util/threadinterrupt.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocked CThreadInterrupt that returns "randomly" whether it is interrupted and never sleeps.
|
||||||
|
*/
|
||||||
|
class FuzzedThreadInterrupt : public CThreadInterrupt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit FuzzedThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider);
|
||||||
|
|
||||||
|
virtual bool interrupted() const override;
|
||||||
|
virtual bool sleep_for(Clock::duration) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FuzzedDataProvider& m_fuzzed_data_provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::shared_ptr<CThreadInterrupt> ConsumeThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider)
|
||||||
|
{
|
||||||
|
return std::make_shared<FuzzedThreadInterrupt>(fuzzed_data_provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITCOIN_TEST_FUZZ_UTIL_THREADINTERRUPT_H
|
|
@ -50,10 +50,10 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
|
||||||
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
|
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
|
||||||
};
|
};
|
||||||
|
|
||||||
CThreadInterrupt interrupt;
|
auto interrupt{std::make_shared<CThreadInterrupt>()};
|
||||||
const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)};
|
const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)};
|
||||||
const Proxy sam_proxy(addr.value(), false);
|
const Proxy sam_proxy(addr.value(), false);
|
||||||
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, &interrupt);
|
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, interrupt);
|
||||||
|
|
||||||
{
|
{
|
||||||
ASSERT_DEBUG_LOG("Creating persistent SAM session");
|
ASSERT_DEBUG_LOG("Creating persistent SAM session");
|
||||||
|
@ -112,12 +112,12 @@ BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
CThreadInterrupt interrupt;
|
auto interrupt{std::make_shared<CThreadInterrupt>()};
|
||||||
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
|
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
|
||||||
const Proxy sam_proxy(addr, false);
|
const Proxy sam_proxy(addr, false);
|
||||||
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
|
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
|
||||||
sam_proxy,
|
sam_proxy,
|
||||||
&interrupt);
|
interrupt);
|
||||||
|
|
||||||
i2p::Connection conn;
|
i2p::Connection conn;
|
||||||
for (size_t i = 0; i < 5; ++i) {
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
@ -155,10 +155,10 @@ BOOST_AUTO_TEST_CASE(damaged_private_key)
|
||||||
"391 bytes"}}) {
|
"391 bytes"}}) {
|
||||||
BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents));
|
BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents));
|
||||||
|
|
||||||
CThreadInterrupt interrupt;
|
auto interrupt{std::make_shared<CThreadInterrupt>()};
|
||||||
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
|
const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
|
||||||
const Proxy sam_proxy{addr, false};
|
const Proxy sam_proxy{addr, false};
|
||||||
i2p::sam::Session session(i2p_private_key_file, sam_proxy, &interrupt);
|
i2p::sam::Session session(i2p_private_key_file, sam_proxy, interrupt);
|
||||||
|
|
||||||
{
|
{
|
||||||
ASSERT_DEBUG_LOG("Creating persistent SAM session");
|
ASSERT_DEBUG_LOG("Creating persistent SAM session");
|
||||||
|
|
|
@ -9,11 +9,16 @@
|
||||||
|
|
||||||
CThreadInterrupt::CThreadInterrupt() : flag(false) {}
|
CThreadInterrupt::CThreadInterrupt() : flag(false) {}
|
||||||
|
|
||||||
CThreadInterrupt::operator bool() const
|
bool CThreadInterrupt::interrupted() const
|
||||||
{
|
{
|
||||||
return flag.load(std::memory_order_acquire);
|
return flag.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CThreadInterrupt::operator bool() const
|
||||||
|
{
|
||||||
|
return interrupted();
|
||||||
|
}
|
||||||
|
|
||||||
void CThreadInterrupt::reset()
|
void CThreadInterrupt::reset()
|
||||||
{
|
{
|
||||||
flag.store(false, std::memory_order_release);
|
flag.store(false, std::memory_order_release);
|
||||||
|
|
|
@ -27,11 +27,27 @@ class CThreadInterrupt
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Clock = std::chrono::steady_clock;
|
using Clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
CThreadInterrupt();
|
CThreadInterrupt();
|
||||||
explicit operator bool() const;
|
|
||||||
void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
|
virtual ~CThreadInterrupt() = default;
|
||||||
void reset();
|
|
||||||
bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
|
/// Return true if `operator()()` has been called.
|
||||||
|
virtual bool interrupted() const;
|
||||||
|
|
||||||
|
/// An alias for `interrupted()`.
|
||||||
|
virtual explicit operator bool() const;
|
||||||
|
|
||||||
|
/// Interrupt any sleeps. After this `interrupted()` will return `true`.
|
||||||
|
virtual void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
|
||||||
|
|
||||||
|
/// Reset to an non-interrupted state.
|
||||||
|
virtual void reset();
|
||||||
|
|
||||||
|
/// Sleep for the given duration.
|
||||||
|
/// @retval true The time passed.
|
||||||
|
/// @retval false The sleep was interrupted.
|
||||||
|
virtual bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::condition_variable cond;
|
std::condition_variable cond;
|
||||||
|
|
Loading…
Reference in a new issue