Skip to content

Commit c3b446a

Browse files
committed
Merge bitcoin/bitcoin#30273: fuzz: FuzzedSock::Recv() don't lose bytes from MSG_PEEK read
4d81b4d fuzz: FuzzedSock::Recv() don't lose bytes from MSG_PEEK read (Vasil Dimov) b51d75e fuzz: simplify FuzzedSock::m_peek_data (Vasil Dimov) Pull request description: Problem: If `FuzzedSock::Recv(N, MSG_PEEK)` is called then `N` bytes would be retrieved from the fuzz provider, saved in `m_peek_data` and returned to the caller (ok). If after this `FuzzedSock::Recv(M, 0)` is called where `M < N` then the first `M` bytes from `m_peek_data` would be returned to the caller (ok), but the remaining `N - M` bytes in `m_peek_data` would be discarded/lost (not ok). They must be returned by a subsequent `Recv()`. To resolve this, only remove the head `N` bytes from `m_peek_data`. --- This is a followup to bitcoin/bitcoin#30211, more specifically: bitcoin/bitcoin#30211 (comment) bitcoin/bitcoin#30211 (comment) ACKs for top commit: marcofleon: ACK 4d81b4d. Tested this with the I2P fuzz target and there's no loss in coverage. I think overall this is an improvement in the robustness of `Recv` in `FuzzedSock`. dergoegge: Code review ACK 4d81b4d brunoerg: utACK 4d81b4d Tree-SHA512: 73b5cb396784652447874998850e45899e8cba49dcd2cc96b2d1f63be78e48201ab88a76cf1c3cb880abac57af07f2c65d673a1021ee1a577d0496c3a4b0c5dd
2 parents 2f81315 + 4d81b4d commit c3b446a

File tree

2 files changed

+36
-36
lines changed

2 files changed

+36
-36
lines changed

src/test/fuzz/util/net.cpp

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -182,54 +182,54 @@ ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
182182
EWOULDBLOCK,
183183
};
184184
assert(buf != nullptr || len == 0);
185+
186+
// Do the latency before any of the "return" statements.
187+
if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
188+
std::this_thread::sleep_for(std::chrono::milliseconds{2});
189+
}
190+
185191
if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
186192
const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
187193
if (r == -1) {
188194
SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
189195
}
190196
return r;
191197
}
192-
std::vector<uint8_t> random_bytes;
193-
bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
194-
if (m_peek_data.has_value()) {
195-
// `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
196-
random_bytes = m_peek_data.value();
198+
199+
size_t copied_so_far{0};
200+
201+
if (!m_peek_data.empty()) {
202+
// `MSG_PEEK` was used in the preceding `Recv()` call, copy the first bytes from `m_peek_data`.
203+
const size_t copy_len{std::min(len, m_peek_data.size())};
204+
std::memcpy(buf, m_peek_data.data(), copy_len);
205+
copied_so_far += copy_len;
197206
if ((flags & MSG_PEEK) == 0) {
198-
m_peek_data.reset();
207+
m_peek_data.erase(m_peek_data.begin(), m_peek_data.begin() + copy_len);
199208
}
200-
pad_to_len_bytes = false;
201-
} else if ((flags & MSG_PEEK) != 0) {
202-
// New call with `MSG_PEEK`.
203-
random_bytes = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len);
204-
if (!random_bytes.empty()) {
205-
m_peek_data = random_bytes;
206-
pad_to_len_bytes = false;
207-
}
208-
} else {
209-
random_bytes = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len);
210209
}
211-
if (random_bytes.empty()) {
212-
const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
213-
if (r == -1) {
214-
SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
215-
}
216-
return r;
210+
211+
if (copied_so_far == len) {
212+
return copied_so_far;
217213
}
218-
// `random_bytes` might exceed the size of `buf` if e.g. Recv is called with
219-
// len=N and MSG_PEEK first and afterwards called with len=M (M < N) and
220-
// without MSG_PEEK.
221-
size_t recv_len{std::min(random_bytes.size(), len)};
222-
std::memcpy(buf, random_bytes.data(), recv_len);
223-
if (pad_to_len_bytes) {
224-
if (len > random_bytes.size()) {
225-
std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
226-
}
227-
return len;
214+
215+
auto new_data = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len - copied_so_far);
216+
if (new_data.empty()) return copied_so_far;
217+
218+
std::memcpy(reinterpret_cast<uint8_t*>(buf) + copied_so_far, new_data.data(), new_data.size());
219+
copied_so_far += new_data.size();
220+
221+
if ((flags & MSG_PEEK) != 0) {
222+
m_peek_data.insert(m_peek_data.end(), new_data.begin(), new_data.end());
228223
}
229-
if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
230-
std::this_thread::sleep_for(std::chrono::milliseconds{2});
224+
225+
if (copied_so_far == len || m_fuzzed_data_provider.ConsumeBool()) {
226+
return copied_so_far;
231227
}
232-
return recv_len;
228+
229+
// Pad to len bytes.
230+
std::memset(reinterpret_cast<uint8_t*>(buf) + copied_so_far, 0x0, len - copied_so_far);
231+
232+
return len;
233233
}
234234

235235
int FuzzedSock::Connect(const sockaddr*, socklen_t) const

src/test/fuzz/util/net.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class FuzzedSock : public Sock
4343
* If `MSG_PEEK` is used, then our `Recv()` returns some random data as usual, but on the next
4444
* `Recv()` call we must return the same data, thus we remember it here.
4545
*/
46-
mutable std::optional<std::vector<uint8_t>> m_peek_data;
46+
mutable std::vector<uint8_t> m_peek_data;
4747

4848
/**
4949
* Whether to pretend that the socket is select(2)-able. This is randomly set in the

0 commit comments

Comments
 (0)