From 865cdf3692590bc6b1121524fe1bee188788b791 Mon Sep 17 00:00:00 2001 From: dergoegge Date: Fri, 31 May 2024 14:26:28 +0100 Subject: [PATCH 1/3] [fuzz] Use fuzzer friendly ConsumeRandomLengthByteVector in FuzzedSock::Recv See comment on FuzzedDataProvider::ConsumeRandomLengthString. --- src/test/fuzz/util/net.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index 99151bb84d..167795e423 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -206,8 +206,7 @@ ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const pad_to_len_bytes = false; } } else { - random_bytes = m_fuzzed_data_provider.ConsumeBytes( - m_fuzzed_data_provider.ConsumeIntegralInRange(0, len)); + random_bytes = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len); } if (random_bytes.empty()) { const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; From a7fceda68bb62fe3d9060fcf52e33b2f64a2acf9 Mon Sep 17 00:00:00 2001 From: dergoegge Date: Fri, 31 May 2024 12:49:13 +0100 Subject: [PATCH 2/3] [fuzz] Make peeking through FuzzedSock::Recv fuzzer friendly FuzzedSock only supports peeking at one byte at a time, which is not fuzzer friendly when trying to receive long data. Fix this by supporting peek data of arbitrary length instead of only one byte. --- src/test/fuzz/util/net.cpp | 14 +++++++++----- src/test/fuzz/util/net.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index 167795e423..cbf549276c 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -193,16 +193,16 @@ ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()}; if (m_peek_data.has_value()) { // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`. - random_bytes.assign({m_peek_data.value()}); + random_bytes = m_peek_data.value(); if ((flags & MSG_PEEK) == 0) { m_peek_data.reset(); } pad_to_len_bytes = false; } else if ((flags & MSG_PEEK) != 0) { // New call with `MSG_PEEK`. - random_bytes = m_fuzzed_data_provider.ConsumeBytes(1); + random_bytes = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len); if (!random_bytes.empty()) { - m_peek_data = random_bytes[0]; + m_peek_data = random_bytes; pad_to_len_bytes = false; } } else { @@ -215,7 +215,11 @@ ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const } return r; } - std::memcpy(buf, random_bytes.data(), random_bytes.size()); + // `random_bytes` might exceed the size of `buf` if e.g. Recv is called with + // len=N and MSG_PEEK first and afterwards called with len=M (M < N) and + // without MSG_PEEK. + size_t recv_len{std::min(random_bytes.size(), len)}; + std::memcpy(buf, random_bytes.data(), recv_len); if (pad_to_len_bytes) { if (len > random_bytes.size()) { std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size()); @@ -225,7 +229,7 @@ ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds{2}); } - return random_bytes.size(); + return recv_len; } int FuzzedSock::Connect(const sockaddr*, socklen_t) const diff --git a/src/test/fuzz/util/net.h b/src/test/fuzz/util/net.h index a6c9e23f2e..ed02680676 100644 --- a/src/test/fuzz/util/net.h +++ b/src/test/fuzz/util/net.h @@ -43,7 +43,7 @@ class FuzzedSock : public Sock * If `MSG_PEEK` is used, then our `Recv()` returns some random data as usual, but on the next * `Recv()` call we must return the same data, thus we remember it here. */ - mutable std::optional m_peek_data; + mutable std::optional> m_peek_data; /** * Whether to pretend that the socket is select(2)-able. This is randomly set in the From 22d0f1a27ef7733b51b3c2138a8d01713df8f248 Mon Sep 17 00:00:00 2001 From: marcofleon Date: Fri, 31 May 2024 14:32:39 +0100 Subject: [PATCH 3/3] [fuzz] Avoid endless waiting in FuzzedSock::{Wait,WaitMany} Currently, when the FuzzedDataProvider of a FuzzedSock runs out of data, FuzzedSock::Wait and WaitMany will simulate endless waiting as the requested events are never simulated as occured. Fix this by simulating event occurence when ConsumeBool() returns false (e.g. when the data provider runs out). Co-authored-by: dergoegge --- src/test/fuzz/util/net.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index cbf549276c..5e7aae670e 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -383,7 +383,10 @@ bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* return false; } if (occurred != nullptr) { - *occurred = m_fuzzed_data_provider.ConsumeBool() ? requested : 0; + // We simulate the requested event as occured when ConsumeBool() + // returns false. This avoids simulating endless waiting if the + // FuzzedDataProvider runs out of data. + *occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : requested; } return true; } @@ -392,7 +395,10 @@ bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& even { for (auto& [sock, events] : events_per_sock) { (void)sock; - events.occurred = m_fuzzed_data_provider.ConsumeBool() ? events.requested : 0; + // We simulate the requested event as occured when ConsumeBool() + // returns false. This avoids simulating endless waiting if the + // FuzzedDataProvider runs out of data. + events.occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : events.requested; } return true; }