/ src / test / fuzz / p2p_transport_serialization.cpp
p2p_transport_serialization.cpp
  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>
 10  #include <test/fuzz/FuzzedDataProvider.h>
 11  #include <test/fuzz/fuzz.h>
 12  #include <test/fuzz/util.h>
 13  #include <test/util/xoroshiro128plusplus.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();
 29      SelectParams(ChainType::REGTEST);
 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) {
159          CSerializedNetMsg msg;
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  }