Coverage Report

Created: 2026-08-14 17:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/ipc/test/fuzz/ipc.cpp
Line
Count
Source
1
// Copyright (c) 2026-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 <primitives/transaction.h>
6
#include <capnp/capability.h>
7
#include <capnp/rpc.h>
8
#include <ipc/util.h>
9
#include <kj/memory.h>
10
#include <mp/proxy-io.h>
11
#include <mp/proxy.h>
12
#include <test/fuzz/FuzzedDataProvider.h>
13
#include <test/fuzz/fuzz.h>
14
#include <ipc/test/fuzz/ipc_fuzz.capnp.h>
15
#include <ipc/test/fuzz/ipc_fuzz.capnp.proxy.h>
16
#include <ipc/test/fuzz/ipc_fuzz.h>
17
#include <test/fuzz/util.h>
18
#include <test/util/setup_common.h>
19
20
#include <future>
21
#include <memory>
22
#include <stdexcept>
23
#include <thread>
24
25
namespace {
26
class IpcFuzzSetup
27
{
28
public:
29
    IpcFuzzSetup()
30
0
    {
31
0
        std::promise<std::unique_ptr<mp::ProxyClient<test::fuzz::messages::IpcFuzzInterface>>> client_promise;
32
0
        auto client_future{client_promise.get_future()};
33
0
        m_loop_thread = std::thread([&client_promise] {
34
0
            mp::EventLoop loop("ipc-fuzz", [](mp::LogMessage message) {
35
0
                if (message.level == mp::Log::Raise) throw std::runtime_error(message.message);
  Branch (35:21): [True: 0, False: 0]
36
0
            });
37
0
            auto pipe = loop.m_io_context.provider->newTwoWayPipe();
38
39
0
            auto server_connection = std::make_unique<mp::Connection>(
40
0
                loop,
41
0
                kj::mv(pipe.ends[0]),
42
0
                [&](mp::Connection& connection) {
43
0
                    auto server_proxy = kj::heap<mp::ProxyServer<test::fuzz::messages::IpcFuzzInterface>>(
44
0
                        std::make_shared<IpcFuzzImplementation>(), connection);
45
0
                    return capnp::Capability::Client(kj::mv(server_proxy));
46
0
                });
47
0
            server_connection->onDisconnect([&] { server_connection.reset(); });
48
49
0
            auto client_connection = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]));
50
0
            auto client_proxy = std::make_unique<mp::ProxyClient<test::fuzz::messages::IpcFuzzInterface>>(
51
0
                client_connection->m_rpc_system->bootstrap(mp::ServerVatId().vat_id)
52
0
                    .castAs<test::fuzz::messages::IpcFuzzInterface>(),
53
0
                client_connection.get(),
54
0
                /* destroy_connection= */ true);
55
0
            (void)client_connection.release();
56
57
0
            client_promise.set_value(std::move(client_proxy));
58
0
            loop.loop();
59
0
        });
60
0
        m_client = client_future.get();
61
0
    }
62
63
    ~IpcFuzzSetup()
64
0
    {
65
0
        m_client.reset();
66
0
        if (m_loop_thread.joinable()) m_loop_thread.join();
  Branch (66:13): [True: 0, False: 0]
67
0
    }
68
69
    std::unique_ptr<mp::ProxyClient<test::fuzz::messages::IpcFuzzInterface>> m_client;
70
71
private:
72
    std::thread m_loop_thread;
73
};
74
75
static IpcFuzzSetup* g_ipc;
76
77
static void initialize_ipc()
78
0
{
79
0
    static const auto testing_setup = MakeNoLogFileContext<>();
80
0
    (void)testing_setup;
81
82
    // Ensure the thread's ThreadContext is created before the IPC setup, so
83
    // it is destroyed after it, since C++ destroys thread_local objects in
84
    // reverse construction order.
85
0
    mp::CurrentThread();
86
87
0
    thread_local static IpcFuzzSetup ipc; // NOLINT(bitcoin-nontrivial-threadlocal)
88
0
    g_ipc = &ipc;
89
0
}
90
91
FUZZ_TARGET(ipc, .init = initialize_ipc)
92
0
{
93
0
    auto& ipc = *g_ipc;
94
0
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
95
0
    LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 64) {
96
0
        CallOneOf(
97
0
            fuzzed_data_provider,
98
0
            [&] {
99
0
                static constexpr int MIN_ADD{-1'000'000};
100
0
                static constexpr int MAX_ADD{1'000'000};
101
0
                const int a = fuzzed_data_provider.ConsumeIntegralInRange<int>(MIN_ADD, MAX_ADD);
102
0
                const int b = fuzzed_data_provider.ConsumeIntegralInRange<int>(MIN_ADD, MAX_ADD);
103
0
                assert(ipc.m_client->add(a, b) == a + b);
  Branch (103:17): [True: 0, False: 0]
104
0
            },
105
0
            [&] {
106
0
                COutPoint outpoint{Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider)),
107
0
                                   fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
108
0
                COutPoint expected{outpoint.hash, outpoint.n ^ 0xFFFFFFFFu};
109
0
                assert(ipc.m_client->passOutPoint(outpoint) == expected);
  Branch (109:17): [True: 0, False: 0]
110
0
            },
111
0
            [&] {
112
0
                std::vector<uint8_t> value = ConsumeRandomLengthByteVector<uint8_t>(fuzzed_data_provider, 512);
113
0
                std::vector<uint8_t> expected{value.rbegin(), value.rend()};
114
0
                assert(ipc.m_client->passVectorUint8(value) == expected);
  Branch (114:17): [True: 0, False: 0]
115
0
            },
116
0
            [&] {
117
0
                CScript script{ConsumeScript(fuzzed_data_provider)};
118
0
                CScript expected{script};
119
0
                expected << OP_NOP;
120
0
                assert(ipc.m_client->passScript(script) == expected);
  Branch (120:17): [True: 0, False: 0]
121
0
            },
122
0
            [&] {
123
0
                UniValue value;
124
0
                if (!value.read(fuzzed_data_provider.ConsumeRandomLengthString(512))) return;
  Branch (124:21): [True: 0, False: 0]
125
0
                assert(ipc.m_client->passUniValue(value).write() == value.write());
  Branch (125:17): [True: 0, False: 0]
126
0
            },
127
0
            [&] {
128
0
                const CMutableTransaction mutable_tx = ConsumeTransaction(fuzzed_data_provider, std::nullopt);
129
0
                if (mutable_tx.vin.empty()) return;
  Branch (129:21): [True: 0, False: 0]
130
0
                const CTransactionRef tx = MakeTransactionRef(mutable_tx);
131
                assert(*ipc.m_client->passTransaction(tx) == *tx);
  Branch (131:17): [True: 0, False: 0]
132
0
            });
133
0
    }
134
0
}
135
} // namespace