fuzz: share a single mocked steady clock across FuzzedSock instances
What changed, and why it matters
This change only affects Bitcoin Core's internal fuzz testing code. It makes several test mock sockets share a single fake clock instead of each having their own, so timing behaves more realistically during automated fuzz testing. There is no change to the live Bitcoin network code, wallets, consensus rules, or anything end users run.
No action required. This is a benign fuzz-test refactoring with no production impact.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors FuzzedSock in src/test/fuzz/util/net.{h,cpp} to take a FakeSteadyClock reference rather than owning its own mocked steady clock and calling MockableSteadyClock::SetMockTime(). All fuzz targets that construct FuzzedSock/ConsumeNode/ConsumeSock are updated to create one FakeSteadyClock per iteration and pass it by reference. This ensures multiple FuzzedSock instances in one fuzz case advance a unified clock and that mock time is reset via FakeSteadyClock’s RAII destructor. The change is purely test infrastructure.
Changed components
src/test/fuzz/util/net.cppsrc/test/fuzz/util/net.hsrc/test/fuzz/cmpctblock.cppsrc/test/fuzz/connman.cppsrc/test/fuzz/i2p.cppsrc/test/fuzz/net.cppsrc/test/fuzz/p2p_handshake.cppsrc/test/fuzz/pcp.cppsrc/test/fuzz/process_message.cppsrc/test/fuzz/process_messages.cppsrc/test/fuzz/socks5.cppInspect captured patch +45 / −40
diff --git a/src/test/fuzz/cmpctblock.cpp b/src/test/fuzz/cmpctblock.cpp
index ff5279ca..0cdc3808 100644
--- a/src/test/fuzz/cmpctblock.cpp
+++ b/src/test/fuzz/cmpctblock.cpp
@@ -164,6 +164,7 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
FakeNodeClock clock{1610000000s};
+ FakeSteadyClock steady_clock;
auto setup = g_setup;
auto& mempool = *setup->m_node.mempool;
@@ -189,7 +190,7 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
std::vector<CNode*> peers;
for (int i = 0; i < 4; ++i) {
- peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release());
+ peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, i).release());
CNode& p2p_node = *peers.back();
FillNode(fuzzed_data_provider, connman, p2p_node);
connman.AddTestNode(p2p_node);
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 614bdf4f..cca9b137 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -42,6 +42,7 @@ FUZZ_TARGET(connman, .init = initialize_connman)
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
+ FakeSteadyClock steady_clock;
auto netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
auto addr_man_ptr{std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio())};
if (fuzzed_data_provider.ConsumeBool()) {
@@ -58,8 +59,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
// Mock CreateSock() to create FuzzedSock.
auto CreateSockOrig = CreateSock;
- CreateSock = [&fuzzed_data_provider](int, int, int) {
- return std::make_unique<FuzzedSock>(fuzzed_data_provider);
+ CreateSock = [&fuzzed_data_provider, &steady_clock](int, int, int) {
+ return std::make_unique<FuzzedSock>(fuzzed_data_provider, steady_clock);
};
// Mock g_dns_lookup() to return a fuzzed address.
@@ -96,13 +97,13 @@ FUZZ_TARGET(connman, .init = initialize_connman)
CNetAddr random_netaddr;
CAddress random_address;
- CNode random_node = ConsumeNode(fuzzed_data_provider);
+ CNode random_node = ConsumeNode(fuzzed_data_provider, steady_clock);
CSubNet random_subnet;
std::string random_string;
std::vector<NodeId> node_ids;
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
- CNode& p2p_node{*ConsumeNodeAsUniquePtr(fuzzed_data_provider).release()};
+ CNode& p2p_node{*ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock).release()};
// Simulate post-handshake state.
p2p_node.fSuccessfullyConnected = true;
connman.AddTestNode(p2p_node);
diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp
index 5c94a19b..1e4be8a9 100644
--- a/src/test/fuzz/i2p.cpp
+++ b/src/test/fuzz/i2p.cpp
@@ -28,11 +28,12 @@ FUZZ_TARGET(i2p, .init = initialize_i2p)
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
+ FakeSteadyClock steady_clock;
// Mock CreateSock() to create FuzzedSock.
auto CreateSockOrig = CreateSock;
- CreateSock = [&fuzzed_data_provider](int, int, int) {
- return std::make_unique<FuzzedSock>(fuzzed_data_provider);
+ CreateSock = [&fuzzed_data_provider, &steady_clock](int, int, int) {
+ return std::make_unique<FuzzedSock>(fuzzed_data_provider, steady_clock);
};
const fs::path private_key_path = gArgs.GetDataDirNet() / "fuzzed_i2p_private_key";
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 93ea00cb..8ee08ed6 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -33,7 +33,8 @@ FUZZ_TARGET(net, .init = initialize_net)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
- CNode node{ConsumeNode(fuzzed_data_provider)};
+ FakeSteadyClock steady_clock;
+ CNode node{ConsumeNode(fuzzed_data_provider, steady_clock)};
node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>());
if (const auto service_opt =
ConsumeDeserializable<CService>(fuzzed_data_provider, ConsumeDeserializationParams<CNetAddr::SerParams>(fuzzed_data_provider)))
@@ -82,8 +83,9 @@ FUZZ_TARGET(local_address, .init = initialize_net)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
+ FakeSteadyClock steady_clock;
CService service{ConsumeService(fuzzed_data_provider)};
- CNode node{ConsumeNode(fuzzed_data_provider)};
+ CNode node{ConsumeNode(fuzzed_data_provider, steady_clock)};
{
LOCK(g_maplocalhost_mutex);
mapLocalHost.clear();
diff --git a/src/test/fuzz/p2p_handshake.cpp b/src/test/fuzz/p2p_handshake.cpp
index affc63c9..e0c8ac16 100644
--- a/src/test/fuzz/p2p_handshake.cpp
+++ b/src/test/fuzz/p2p_handshake.cpp
@@ -42,6 +42,7 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize)
auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
FakeNodeClock clock{1610000000s}; // any time to successfully reset ibd
+ FakeSteadyClock steady_clock;
chainman.ResetIbd();
node.banman.reset();
@@ -64,7 +65,7 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize)
std::vector<CNode*> peers;
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
- peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release());
+ peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, i).release());
connman.AddTestNode(*peers.back());
node.peerman->InitializeNode(
*peers.back(),
diff --git a/src/test/fuzz/pcp.cpp b/src/test/fuzz/pcp.cpp
index a656d443..221d237d 100644
--- a/src/test/fuzz/pcp.cpp
+++ b/src/test/fuzz/pcp.cpp
@@ -6,6 +6,7 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/fuzz/util/net.h>
+#include <test/util/time.h>
#include <common/pcp.h>
#include <logging.h>
@@ -31,11 +32,12 @@ void port_map_target_init()
FUZZ_TARGET(pcp_request_port_map, .init = port_map_target_init)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ FakeSteadyClock steady_clock;
// Create a mocked socket between random (and potentially invalid) client and gateway addresses.
CreateSock = [&](int domain, int type, int protocol) {
if ((domain == AF_INET || domain == AF_INET6) && type == SOCK_DGRAM && protocol == IPPROTO_UDP) {
- return std::make_unique<FuzzedSock>(fuzzed_data_provider);
+ return std::make_unique<FuzzedSock>(fuzzed_data_provider, steady_clock);
}
return std::unique_ptr<FuzzedSock>();
};
@@ -59,11 +61,12 @@ FUZZ_TARGET(pcp_request_port_map, .init = port_map_target_init)
FUZZ_TARGET(natpmp_request_port_map, .init = port_map_target_init)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ FakeSteadyClock steady_clock;
// Create a mocked socket between random (and potentially invalid) client and gateway addresses.
CreateSock = [&](int domain, int type, int protocol) {
if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) {
- return std::make_unique<FuzzedSock>(fuzzed_data_provider);
+ return std::make_unique<FuzzedSock>(fuzzed_data_provider, steady_clock);
}
return std::unique_ptr<FuzzedSock>();
};
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index f5d59891..7712ea6f 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -84,6 +84,7 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
FakeNodeClock clock{1610000000s}; // any time to successfully reset ibd
+ FakeSteadyClock steady_clock;
chainman.ResetIbd();
chainman.DisableNextWrite();
@@ -111,7 +112,7 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
node.validation_signals->RegisterValidationInterface(node.peerman.get());
- CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider).release();
+ CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock).release();
connman.AddTestNode(p2p_node);
FillNode(fuzzed_data_provider, connman, p2p_node);
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 68eb2b8d..fd0c9abf 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -73,6 +73,7 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
FakeNodeClock clock{1610000000s}; // any time to successfully reset ibd
+ FakeSteadyClock steady_clock;
chainman.ResetIbd();
chainman.DisableNextWrite();
@@ -98,7 +99,7 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
std::vector<CNode*> peers;
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
- peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release());
+ peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, i).release());
CNode& p2p_node = *peers.back();
FillNode(fuzzed_data_provider, connman, p2p_node);
diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp
index 0bd1710d..8a4592c8 100644
--- a/src/test/fuzz/socks5.cpp
+++ b/src/test/fuzz/socks5.cpp
@@ -32,6 +32,7 @@ FUZZ_TARGET(socks5, .init = initialize_socks5)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
+ FakeSteadyClock steady_clock;
ProxyCredentials proxy_credentials;
proxy_credentials.username = fuzzed_data_provider.ConsumeRandomLengthString(512);
proxy_credentials.password = fuzzed_data_provider.ConsumeRandomLengthString(512);
@@ -41,7 +42,7 @@ FUZZ_TARGET(socks5, .init = initialize_socks5)
// Set FUZZED_SOCKET_FAKE_LATENCY=1 to exercise recv timeout code paths. This
// will slow down fuzzing.
g_socks5_recv_timeout = (fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) ? 1ms : default_socks5_recv_timeout;
- FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider);
+ FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider, steady_clock);
// This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within
// a few seconds of fuzzing.
auto str_dest = fuzzed_data_provider.ConsumeRandomLengthString(512);
diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp
index ba17e4e9..dfd295b3 100644
--- a/src/test/fuzz/util/net.cpp
+++ b/src/test/fuzz/util/net.cpp
@@ -111,13 +111,12 @@ P ConsumeDeserializationParams(FuzzedDataProvider& fuzzed_data_provider) noexcep
template CNetAddr::SerParams ConsumeDeserializationParams(FuzzedDataProvider&) noexcept;
template CAddress::SerParams ConsumeDeserializationParams(FuzzedDataProvider&) noexcept;
-FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
+FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock)
: Sock{fuzzed_data_provider.ConsumeIntegralInRange<SOCKET>(INVALID_SOCKET - 1, INVALID_SOCKET)},
m_fuzzed_data_provider{fuzzed_data_provider},
m_selectable{fuzzed_data_provider.ConsumeBool()},
- m_time{MockableSteadyClock::INITIAL_MOCK_TIME}
+ m_clock{clock}
{
- ElapseTime(std::chrono::seconds(0)); // start mocking the steady clock.
}
FuzzedSock::~FuzzedSock()
@@ -129,12 +128,6 @@ FuzzedSock::~FuzzedSock()
m_socket = INVALID_SOCKET;
}
-void FuzzedSock::ElapseTime(std::chrono::milliseconds duration) const
-{
- m_time += duration;
- MockableSteadyClock::SetMockTime(m_time);
-}
-
FuzzedSock& FuzzedSock::operator=(Sock&& other)
{
assert(false && "Move of Sock into FuzzedSock not allowed.");
@@ -340,7 +333,7 @@ std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) co
}
}
}
- return std::make_unique<FuzzedSock>(m_fuzzed_data_provider);
+ return std::make_unique<FuzzedSock>(m_fuzzed_data_provider, m_clock);
}
int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
@@ -428,7 +421,7 @@ bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event*
// FuzzedDataProvider runs out of data.
*occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : requested;
}
- ElapseTime(timeout);
+ m_clock += timeout;
return true;
}
@@ -441,7 +434,7 @@ bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& even
// FuzzedDataProvider runs out of data.
events.occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : events.requested;
}
- ElapseTime(timeout);
+ m_clock += timeout;
return true;
}
diff --git a/src/test/fuzz/util/net.h b/src/test/fuzz/util/net.h
index 36393811..520c6732 100644
--- a/src/test/fuzz/util/net.h
+++ b/src/test/fuzz/util/net.h
@@ -17,6 +17,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/util.h>
#include <test/util/net.h>
+#include <test/util/time.h>
#include <util/asmap.h>
#include <util/sock.h>
@@ -178,17 +179,16 @@ class FuzzedSock : public Sock
const bool m_selectable;
/**
- * Used to mock the steady clock in methods waiting for a given duration.
+ * Externally-provided context used to mock the steady clock in methods
+ * waiting for a given duration. It is a reference (rather than an owned
+ * member) so that several FuzzedSock instances sharing a test case (e.g.
+ * one per peer, or one created from Accept()) advance a single mocked
+ * clock.
*/
- mutable std::chrono::milliseconds m_time;
-
- /**
- * Set the value of the mocked steady clock such as that many ms have passed.
- */
- void ElapseTime(std::chrono::milliseconds duration) const;
+ FakeSteadyClock& m_clock;
public:
- explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider);
+ explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock);
~FuzzedSock() override;
@@ -228,9 +228,9 @@ public:
return FuzzedNetEvents{fdp};
}
-[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
+[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock)
{
- return FuzzedSock{fuzzed_data_provider};
+ return FuzzedSock{fuzzed_data_provider, clock};
}
[[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept
@@ -267,10 +267,10 @@ inline std::vector<CService> ConsumeServiceVector(FuzzedDataProvider& fuzzed_dat
CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept;
template <bool ReturnUniquePtr = false>
-auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept
+auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, FakeSteadyClock& clock, const std::optional<NodeId>& node_id_in = std::nullopt) noexcept
{
const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, std::numeric_limits<NodeId>::max()));
- const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider);
+ const auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider, clock);
const CAddress address = ConsumeAddress(fuzzed_data_provider);
const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
@@ -307,7 +307,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
CNodeOptions{ .permission_flags = permission_flags }};
}
}
-inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, node_id_in); }
+inline std::unique_ptr<CNode> ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, FakeSteadyClock& clock, const std::optional<NodeId>& node_id_in = std::nullopt) { return ConsumeNode<true>(fdp, clock, node_id_in); }
void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.