Bitcoin Core  27.99.0
P2P Digital Currency
p2p_transport_serialization.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <chainparams.h>
6 #include <hash.h>
7 #include <net.h>
8 #include <netmessagemaker.h>
9 #include <protocol.h>
11 #include <test/fuzz/fuzz.h>
12 #include <test/fuzz/util.h>
14 #include <util/chaintype.h>
15 
16 #include <cassert>
17 #include <cstdint>
18 #include <limits>
19 #include <optional>
20 #include <vector>
21 
22 namespace {
23 
24 std::vector<std::string> g_all_messages;
25 
26 void initialize_p2p_transport_serialization()
27 {
28  ECC_Start();
30  g_all_messages = getAllNetMessageTypes();
31  std::sort(g_all_messages.begin(), g_all_messages.end());
32 }
33 
34 } // namespace
35 
36 FUZZ_TARGET(p2p_transport_serialization, .init = initialize_p2p_transport_serialization)
37 {
38  // Construct transports for both sides, with dummy NodeIds.
39  V1Transport recv_transport{NodeId{0}};
40  V1Transport send_transport{NodeId{1}};
41 
42  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
43 
44  auto checksum_assist = fuzzed_data_provider.ConsumeBool();
45  auto magic_bytes_assist = fuzzed_data_provider.ConsumeBool();
46  std::vector<uint8_t> mutable_msg_bytes;
47 
48  auto header_bytes_remaining = CMessageHeader::HEADER_SIZE;
49  if (magic_bytes_assist) {
50  auto msg_start = Params().MessageStart();
51  for (size_t i = 0; i < CMessageHeader::MESSAGE_SIZE_SIZE; ++i) {
52  mutable_msg_bytes.push_back(msg_start[i]);
53  }
54  header_bytes_remaining -= CMessageHeader::MESSAGE_SIZE_SIZE;
55  }
56 
57  if (checksum_assist) {
58  header_bytes_remaining -= CMessageHeader::CHECKSUM_SIZE;
59  }
60 
61  auto header_random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(header_bytes_remaining);
62  mutable_msg_bytes.insert(mutable_msg_bytes.end(), header_random_bytes.begin(), header_random_bytes.end());
63  auto payload_bytes = fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
64 
65  if (checksum_assist && mutable_msg_bytes.size() == CMessageHeader::CHECKSUM_OFFSET) {
66  CHash256 hasher;
67  unsigned char hsh[32];
68  hasher.Write(payload_bytes);
69  hasher.Finalize(hsh);
70  for (size_t i = 0; i < CMessageHeader::CHECKSUM_SIZE; ++i) {
71  mutable_msg_bytes.push_back(hsh[i]);
72  }
73  }
74 
75  mutable_msg_bytes.insert(mutable_msg_bytes.end(), payload_bytes.begin(), payload_bytes.end());
76  Span<const uint8_t> msg_bytes{mutable_msg_bytes};
77  while (msg_bytes.size() > 0) {
78  if (!recv_transport.ReceivedBytes(msg_bytes)) {
79  break;
80  }
81  if (recv_transport.ReceivedMessageComplete()) {
82  const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
83  bool reject_message{false};
84  CNetMessage msg = recv_transport.GetReceivedMessage(m_time, reject_message);
85  assert(msg.m_type.size() <= CMessageHeader::COMMAND_SIZE);
86  assert(msg.m_raw_message_size <= mutable_msg_bytes.size());
87  assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
88  assert(msg.m_time == m_time);
89 
90  std::vector<unsigned char> header;
91  auto msg2 = NetMsg::Make(msg.m_type, Span{msg.m_recv});
92  bool queued = send_transport.SetMessageToSend(msg2);
93  assert(queued);
94  std::optional<bool> known_more;
95  while (true) {
96  const auto& [to_send, more, _msg_type] = send_transport.GetBytesToSend(false);
97  if (known_more) assert(!to_send.empty() == *known_more);
98  if (to_send.empty()) break;
99  send_transport.MarkBytesSent(to_send.size());
100  known_more = more;
101  }
102  }
103  }
104 }
105 
106 namespace {
107 
108 template<typename R>
109 void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDataProvider& provider)
110 {
111  // Simulation test with two Transport objects, which send messages to each other, with
112  // sending and receiving fragmented into multiple pieces that may be interleaved. It primarily
113  // verifies that the sending and receiving side are compatible with each other, plus a few
114  // sanity checks. It does not attempt to introduce errors in the communicated data.
115 
116  // Put the transports in an array for by-index access.
117  const std::array<Transport*, 2> transports = {&initiator, &responder};
118 
119  // Two vectors representing in-flight bytes. inflight[i] is from transport[i] to transport[!i].
120  std::array<std::vector<uint8_t>, 2> in_flight;
121 
122  // Two queues with expected messages. expected[i] is expected to arrive in transport[!i].
123  std::array<std::deque<CSerializedNetMsg>, 2> expected;
124 
125  // Vectors with bytes last returned by GetBytesToSend() on transport[i].
126  std::array<std::vector<uint8_t>, 2> to_send;
127 
128  // Last returned 'more' values (if still relevant) by transport[i]->GetBytesToSend(), for
129  // both have_next_message false and true.
130  std::array<std::optional<bool>, 2> last_more, last_more_next;
131 
132  // Whether more bytes to be sent are expected on transport[i], before and after
133  // SetMessageToSend().
134  std::array<std::optional<bool>, 2> expect_more, expect_more_next;
135 
136  // Function to consume a message type.
137  auto msg_type_fn = [&]() {
138  uint8_t v = provider.ConsumeIntegral<uint8_t>();
139  if (v == 0xFF) {
140  // If v is 0xFF, construct a valid (but possibly unknown) message type from the fuzz
141  // data.
142  std::string ret;
143  while (ret.size() < CMessageHeader::COMMAND_SIZE) {
144  char c = provider.ConsumeIntegral<char>();
145  // Match the allowed characters in CMessageHeader::IsCommandValid(). Any other
146  // character is interpreted as end.
147  if (c < ' ' || c > 0x7E) break;
148  ret += c;
149  }
150  return ret;
151  } else {
152  // Otherwise, use it as index into the list of known messages.
153  return g_all_messages[v % g_all_messages.size()];
154  }
155  };
156 
157  // Function to construct a CSerializedNetMsg to send.
158  auto make_msg_fn = [&](bool first) {
160  if (first) {
161  // Always send a "version" message as first one.
162  msg.m_type = "version";
163  } else {
164  msg.m_type = msg_type_fn();
165  }
166  // Determine size of message to send (limited to 75 kB for performance reasons).
167  size_t size = provider.ConsumeIntegralInRange<uint32_t>(0, 75000);
168  // Get payload of message from RNG.
169  msg.data.resize(size);
170  for (auto& v : msg.data) v = uint8_t(rng());
171  // Return.
172  return msg;
173  };
174 
175  // The next message to be sent (initially version messages, but will be replaced once sent).
176  std::array<CSerializedNetMsg, 2> next_msg = {
177  make_msg_fn(/*first=*/true),
178  make_msg_fn(/*first=*/true)
179  };
180 
181  // Wrapper around transport[i]->GetBytesToSend() that performs sanity checks.
182  auto bytes_to_send_fn = [&](int side) -> Transport::BytesToSend {
183  // Invoke GetBytesToSend twice (for have_next_message = {false, true}). This function does
184  // not modify state (it's const), and only the "more" return value should differ between
185  // the calls.
186  const auto& [bytes, more_nonext, msg_type] = transports[side]->GetBytesToSend(false);
187  const auto& [bytes_next, more_next, msg_type_next] = transports[side]->GetBytesToSend(true);
188  // Compare with expected more.
189  if (expect_more[side].has_value()) assert(!bytes.empty() == *expect_more[side]);
190  // Verify consistency between the two results.
191  assert(bytes == bytes_next);
192  assert(msg_type == msg_type_next);
193  if (more_nonext) assert(more_next);
194  // Compare with previously reported output.
195  assert(to_send[side].size() <= bytes.size());
196  assert(to_send[side] == Span{bytes}.first(to_send[side].size()));
197  to_send[side].resize(bytes.size());
198  std::copy(bytes.begin(), bytes.end(), to_send[side].begin());
199  // Remember 'more' results.
200  last_more[side] = {more_nonext};
201  last_more_next[side] = {more_next};
202  // Return.
203  return {bytes, more_nonext, msg_type};
204  };
205 
206  // Function to make side send a new message.
207  auto new_msg_fn = [&](int side) {
208  // Don't do anything if there are too many unreceived messages already.
209  if (expected[side].size() >= 16) return;
210  // Try to send (a copy of) the message in next_msg[side].
211  CSerializedNetMsg msg = next_msg[side].Copy();
212  bool queued = transports[side]->SetMessageToSend(msg);
213  // Update expected more data.
214  expect_more[side] = expect_more_next[side];
215  expect_more_next[side] = std::nullopt;
216  // Verify consistency of GetBytesToSend after SetMessageToSend
217  bytes_to_send_fn(/*side=*/side);
218  if (queued) {
219  // Remember that this message is now expected by the receiver.
220  expected[side].emplace_back(std::move(next_msg[side]));
221  // Construct a new next message to send.
222  next_msg[side] = make_msg_fn(/*first=*/false);
223  }
224  };
225 
226  // Function to make side send out bytes (if any).
227  auto send_fn = [&](int side, bool everything = false) {
228  const auto& [bytes, more, msg_type] = bytes_to_send_fn(/*side=*/side);
229  // Don't do anything if no bytes to send.
230  if (bytes.empty()) return false;
231  size_t send_now = everything ? bytes.size() : provider.ConsumeIntegralInRange<size_t>(0, bytes.size());
232  if (send_now == 0) return false;
233  // Add bytes to the in-flight queue, and mark those bytes as consumed.
234  in_flight[side].insert(in_flight[side].end(), bytes.begin(), bytes.begin() + send_now);
235  transports[side]->MarkBytesSent(send_now);
236  // If all to-be-sent bytes were sent, move last_more data to expect_more data.
237  if (send_now == bytes.size()) {
238  expect_more[side] = last_more[side];
239  expect_more_next[side] = last_more_next[side];
240  }
241  // Remove the bytes from the last reported to-be-sent vector.
242  assert(to_send[side].size() >= send_now);
243  to_send[side].erase(to_send[side].begin(), to_send[side].begin() + send_now);
244  // Verify that GetBytesToSend gives a result consistent with earlier.
245  bytes_to_send_fn(/*side=*/side);
246  // Return whether anything was sent.
247  return send_now > 0;
248  };
249 
250  // Function to make !side receive bytes (if any).
251  auto recv_fn = [&](int side, bool everything = false) {
252  // Don't do anything if no bytes in flight.
253  if (in_flight[side].empty()) return false;
254  // Decide span to receive
255  size_t to_recv_len = in_flight[side].size();
256  if (!everything) to_recv_len = provider.ConsumeIntegralInRange<size_t>(0, to_recv_len);
257  Span<const uint8_t> to_recv = Span{in_flight[side]}.first(to_recv_len);
258  // Process those bytes
259  while (!to_recv.empty()) {
260  size_t old_len = to_recv.size();
261  bool ret = transports[!side]->ReceivedBytes(to_recv);
262  // Bytes must always be accepted, as this test does not introduce any errors in
263  // communication.
264  assert(ret);
265  // Clear cached expected 'more' information: if certainly no more data was to be sent
266  // before, receiving bytes makes this uncertain.
267  if (expect_more[!side] == false) expect_more[!side] = std::nullopt;
268  if (expect_more_next[!side] == false) expect_more_next[!side] = std::nullopt;
269  // Verify consistency of GetBytesToSend after ReceivedBytes
270  bytes_to_send_fn(/*side=*/!side);
271  bool progress = to_recv.size() < old_len;
272  if (transports[!side]->ReceivedMessageComplete()) {
273  bool reject{false};
274  auto received = transports[!side]->GetReceivedMessage({}, reject);
275  // Receiving must succeed.
276  assert(!reject);
277  // There must be a corresponding expected message.
278  assert(!expected[side].empty());
279  // The m_message_size field must be correct.
280  assert(received.m_message_size == received.m_recv.size());
281  // The m_type must match what is expected.
282  assert(received.m_type == expected[side].front().m_type);
283  // The data must match what is expected.
284  assert(MakeByteSpan(received.m_recv) == MakeByteSpan(expected[side].front().data));
285  expected[side].pop_front();
286  progress = true;
287  }
288  // Progress must be made (by processing incoming bytes and/or returning complete
289  // messages) until all received bytes are processed.
290  assert(progress);
291  }
292  // Remove the processed bytes from the in_flight buffer.
293  in_flight[side].erase(in_flight[side].begin(), in_flight[side].begin() + to_recv_len);
294  // Return whether anything was received.
295  return to_recv_len > 0;
296  };
297 
298  // Main loop, interleaving new messages, sends, and receives.
299  LIMITED_WHILE(provider.remaining_bytes(), 1000) {
300  CallOneOf(provider,
301  // (Try to) give the next message to the transport.
302  [&] { new_msg_fn(/*side=*/0); },
303  [&] { new_msg_fn(/*side=*/1); },
304  // (Try to) send some bytes from the transport to the network.
305  [&] { send_fn(/*side=*/0); },
306  [&] { send_fn(/*side=*/1); },
307  // (Try to) receive bytes from the network, converting to messages.
308  [&] { recv_fn(/*side=*/0); },
309  [&] { recv_fn(/*side=*/1); }
310  );
311  }
312 
313  // When we're done, perform sends and receives of existing messages to flush anything already
314  // in flight.
315  while (true) {
316  bool any = false;
317  if (send_fn(/*side=*/0, /*everything=*/true)) any = true;
318  if (send_fn(/*side=*/1, /*everything=*/true)) any = true;
319  if (recv_fn(/*side=*/0, /*everything=*/true)) any = true;
320  if (recv_fn(/*side=*/1, /*everything=*/true)) any = true;
321  if (!any) break;
322  }
323 
324  // Make sure nothing is left in flight.
325  assert(in_flight[0].empty());
326  assert(in_flight[1].empty());
327 
328  // Make sure all expected messages were received.
329  assert(expected[0].empty());
330  assert(expected[1].empty());
331 
332  // Compare session IDs.
333  assert(transports[0]->GetInfo().session_id == transports[1]->GetInfo().session_id);
334 }
335 
336 std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept
337 {
338  return std::make_unique<V1Transport>(nodeid);
339 }
340 
341 template<typename RNG>
342 std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)
343 {
344  // Retrieve key
345  auto key = ConsumePrivateKey(provider);
346  if (!key.IsValid()) return {};
347  // Construct garbage
348  size_t garb_len = provider.ConsumeIntegralInRange<size_t>(0, V2Transport::MAX_GARBAGE_LEN);
349  std::vector<uint8_t> garb;
350  if (garb_len <= 64) {
351  // When the garbage length is up to 64 bytes, read it directly from the fuzzer input.
352  garb = provider.ConsumeBytes<uint8_t>(garb_len);
353  garb.resize(garb_len);
354  } else {
355  // If it's longer, generate it from the RNG. This avoids having large amounts of
356  // (hopefully) irrelevant data needing to be stored in the fuzzer data.
357  garb.resize(garb_len);
358  for (auto& v : garb) v = uint8_t(rng());
359  }
360  // Retrieve entropy
361  auto ent = provider.ConsumeBytes<std::byte>(32);
362  ent.resize(32);
363  // Use as entropy SHA256(ent || garbage). This prevents a situation where the fuzzer manages to
364  // include the garbage terminator (which is a function of both ellswift keys) in the garbage.
365  // This is extremely unlikely (~2^-116) with random keys/garbage, but the fuzzer can choose
366  // both non-randomly and dependently. Since the entropy is hashed anyway inside the ellswift
367  // computation, no coverage should be lost by using a hash as entropy, and it removes the
368  // possibility of garbage that happens to contain what is effectively a hash of the keys.
369  CSHA256().Write(UCharCast(ent.data()), ent.size())
370  .Write(garb.data(), garb.size())
371  .Finalize(UCharCast(ent.data()));
372 
373  return std::make_unique<V2Transport>(nodeid, initiator, key, ent, std::move(garb));
374 }
375 
376 } // namespace
377 
378 FUZZ_TARGET(p2p_transport_bidirectional, .init = initialize_p2p_transport_serialization)
379 {
380  // Test with two V1 transports talking to each other.
381  FuzzedDataProvider provider{buffer.data(), buffer.size()};
382  XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
383  auto t1 = MakeV1Transport(NodeId{0});
384  auto t2 = MakeV1Transport(NodeId{1});
385  if (!t1 || !t2) return;
386  SimulationTest(*t1, *t2, rng, provider);
387 }
388 
389 FUZZ_TARGET(p2p_transport_bidirectional_v2, .init = initialize_p2p_transport_serialization)
390 {
391  // Test with two V2 transports talking to each other.
392  FuzzedDataProvider provider{buffer.data(), buffer.size()};
393  XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
394  auto t1 = MakeV2Transport(NodeId{0}, true, rng, provider);
395  auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
396  if (!t1 || !t2) return;
397  SimulationTest(*t1, *t2, rng, provider);
398 }
399 
400 FUZZ_TARGET(p2p_transport_bidirectional_v1v2, .init = initialize_p2p_transport_serialization)
401 {
402  // Test with a V1 initiator talking to a V2 responder.
403  FuzzedDataProvider provider{buffer.data(), buffer.size()};
404  XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
405  auto t1 = MakeV1Transport(NodeId{0});
406  auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
407  if (!t1 || !t2) return;
408  SimulationTest(*t1, *t2, rng, provider);
409 }
int ret
ECC_Start()
Definition: key.cpp:435
const CChainParams & Params()
Return the currently selected parameters.
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain type.
const MessageStartChars & MessageStart() const
Definition: chainparams.h:94
A hasher class for Bitcoin's 256-bit hash (double SHA-256).
Definition: hash.h:24
CHash256 & Write(Span< const unsigned char > input)
Definition: hash.h:37
void Finalize(Span< unsigned char > output)
Definition: hash.h:30
static constexpr size_t CHECKSUM_OFFSET
Definition: protocol.h:35
static constexpr size_t CHECKSUM_SIZE
Definition: protocol.h:33
static constexpr size_t MESSAGE_SIZE_SIZE
Definition: protocol.h:32
static constexpr size_t HEADER_SIZE
Definition: protocol.h:36
static constexpr size_t COMMAND_SIZE
Definition: protocol.h:31
Transport protocol agnostic message container.
Definition: net.h:233
A hasher class for SHA-256.
Definition: sha256.h:14
void Finalize(unsigned char hash[OUTPUT_SIZE])
Definition: sha256.cpp:728
CSHA256 & Write(const unsigned char *data, size_t len)
Definition: sha256.cpp:702
std::vector< T > ConsumeBytes(size_t num_bytes)
T ConsumeIntegralInRange(T min, T max)
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
CONSTEXPR_IF_NOT_DEBUG Span< C > first(std::size_t count) const noexcept
Definition: span.h:205
The Transport converts one connection's sent messages to wire bytes, and received bytes back.
Definition: net.h:253
std::tuple< Span< const uint8_t >, bool, const std::string & > BytesToSend
Return type for GetBytesToSend, consisting of:
Definition: net.h:310
static constexpr uint32_t MAX_GARBAGE_LEN
Definition: net.h:633
xoroshiro128++ PRNG.
void SimulationTest(CCoinsView *base, bool fake_best_block)
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:23
CSerializedNetMsg Make(std::string msg_type, Args &&... args)
int64_t NodeId
Definition: net.h:97
FUZZ_TARGET(p2p_transport_serialization,.init=initialize_p2p_transport_serialization)
const std::vector< std::string > & getAllNetMessageTypes()
Definition: protocol.cpp:167
unsigned char * UCharCast(char *c)
Definition: span.h:288
Span< const std::byte > MakeByteSpan(V &&v) noexcept
Definition: span.h:277
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:227
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it)
Definition: txmempool.cpp:810
assert(!tx.IsCoinBase())