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