Coverage Report

Created: 2025-02-21 14:37

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