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