mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-07-26 08:08:45 +02:00
fuzz: share a single mocked steady clock across FuzzedSock instances
Each FuzzedSock used to own its mocked steady clock and call MockableSteadyClock::SetMockTime() directly. Hold the clock by reference to an externally provided FakeSteadyClock instead, so that several FuzzedSock instances sharing a test case (e.g. one per peer, or one created via Accept()) advance a single mocked clock, and the mocking goes through the FakeSteadyClock RAII helper that resets mocktime on destruction. FakeSteadyClock is a LimitOne type, so each fuzz target constructs one instance per iteration and passes it to ConsumeSock / ConsumeNode / the FuzzedSock constructor.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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>();
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user