Branch data Line data Source code
1 : : // Copyright (c) 2021-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 <base58.h>
6 : : #include <key.h>
7 : : #include <key_io.h>
8 : : #include <primitives/block.h>
9 : : #include <primitives/transaction.h>
10 : : #include <psbt.h>
11 : : #include <rpc/request.h>
12 : : #include <rpc/server.h>
13 : : #include <span.h>
14 : : #include <streams.h>
15 : : #include <test/fuzz/FuzzedDataProvider.h>
16 : : #include <test/fuzz/fuzz.h>
17 : : #include <test/fuzz/util.h>
18 : : #include <test/util/setup_common.h>
19 : : #include <test/util/time.h>
20 : : #include <tinyformat.h>
21 : : #include <uint256.h>
22 : : #include <univalue.h>
23 : : #include <util/strencodings.h>
24 : : #include <util/string.h>
25 : : #include <util/time.h>
26 : :
27 : : #include <algorithm>
28 : : #include <cassert>
29 : : #include <cstdint>
30 : : #include <cstdlib>
31 : : #include <exception>
32 : : #include <iostream>
33 : : #include <memory>
34 : : #include <optional>
35 : : #include <stdexcept>
36 : : #include <utility>
37 : : #include <vector>
38 : :
39 : : enum class ChainType;
40 : :
41 : : namespace {
42 : 1 : struct RPCFuzzTestingSetup : public TestingSetup {
43 [ + - ]: 1 : RPCFuzzTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
44 : : {
45 : 1 : }
46 : :
47 : 19761 : void CallRPC(const std::string& rpc_method, UniValue&& params)
48 : : {
49 : 19761 : JSONRPCRequest request;
50 : 19761 : request.context = &m_node;
51 [ + - ]: 19761 : request.strMethod = rpc_method;
52 : 19761 : request.params = std::move(params);
53 [ + + ]: 19761 : tableRPC.execute(request);
54 : 19761 : }
55 : :
56 : 1 : std::vector<std::string> GetRPCCommands() const
57 : : {
58 : 1 : return tableRPC.listCommands();
59 : : }
60 : : };
61 : :
62 : : RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
63 : : std::string g_limit_to_rpc_command;
64 : :
65 : : // RPC commands which are not appropriate for fuzzing: such as RPC commands
66 : : // reading or writing to a filename passed as an RPC parameter, RPC commands
67 : : // resulting in network activity, etc.
68 : : const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
69 : : "addconnection", // avoid DNS lookups
70 : : "addnode", // avoid DNS lookups
71 : : "addpeeraddress", // avoid DNS lookups
72 : : "dumptxoutset", // avoid writing to disk
73 : : "enumeratesigners",
74 : : "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
75 : : "exportasmap", // avoid writing to disk
76 : : "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
77 : : "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
78 : : "gettxoutproof", // avoid prohibitively slow execution
79 : : "importmempool", // avoid reading from disk
80 : : "loadtxoutset", // avoid reading from disk
81 : : "loadwallet", // avoid reading from disk
82 : : "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
83 : : "setban", // avoid DNS lookups
84 : : "stop", // avoid shutdown state
85 : : };
86 : :
87 : : // RPC commands which are safe for fuzzing.
88 : : const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
89 : : "abortprivatebroadcast",
90 : : "analyzepsbt",
91 : : "clearbanned",
92 : : "combinepsbt",
93 : : "combinerawtransaction",
94 : : "converttopsbt",
95 : : "createmultisig",
96 : : "createpsbt",
97 : : "createrawtransaction",
98 : : "decodepsbt",
99 : : "decoderawtransaction",
100 : : "decodescript",
101 : : "deriveaddresses",
102 : : "descriptorprocesspsbt",
103 : : "disconnectnode",
104 : : "echo",
105 : : "echojson",
106 : : "estimaterawfee",
107 : : "estimatesmartfee",
108 : : "finalizepsbt",
109 : : "generate",
110 : : "generateblock",
111 : : "getaddednodeinfo",
112 : : "getaddrmaninfo",
113 : : "getbestblockhash",
114 : : "getblock",
115 : : "getblockchaininfo",
116 : : "getblockcount",
117 : : "getblockfilter",
118 : : "getblockfrompeer", // when no peers are connected, no p2p message is sent
119 : : "getblockhash",
120 : : "getblockheader",
121 : : "getblockstats",
122 : : "getblocktemplate",
123 : : "getchaintips",
124 : : "getchainstates",
125 : : "getchaintxstats",
126 : : "getconnectioncount",
127 : : "getdeploymentinfo",
128 : : "getdescriptoractivity",
129 : : "getdescriptorinfo",
130 : : "getdifficulty",
131 : : "getindexinfo",
132 : : "getmemoryinfo",
133 : : "getmempoolancestors",
134 : : "getmempooldescendants",
135 : : "getmempoolentry",
136 : : "getmempoolfeeratediagram",
137 : : "getmempoolcluster",
138 : : "getmempoolinfo",
139 : : "getmininginfo",
140 : : "getnettotals",
141 : : "getnetworkhashps",
142 : : "getnetworkinfo",
143 : : "getnodeaddresses",
144 : : "getopenrpcinfo",
145 : : "getorphantxs",
146 : : "getpeerinfo",
147 : : "getprioritisedtransactions",
148 : : "getprivatebroadcastinfo",
149 : : "getrawaddrman",
150 : : "getrawmempool",
151 : : "getrawtransaction",
152 : : "getrpcinfo",
153 : : "gettxout",
154 : : "gettxoutsetinfo",
155 : : "gettxspendingprevout",
156 : : "help",
157 : : "invalidateblock",
158 : : "joinpsbts",
159 : : "listbanned",
160 : : "logging",
161 : : "mockscheduler",
162 : : "ping",
163 : : "preciousblock",
164 : : "prioritisetransaction",
165 : : "pruneblockchain",
166 : : "reconsiderblock",
167 : : "rpc.discover",
168 : : "scanblocks",
169 : : "scantxoutset",
170 : : "sendmsgtopeer", // when no peers are connected, no p2p message is sent
171 : : "sendrawtransaction",
172 : : "setmocktime",
173 : : "setnetworkactive",
174 : : "signmessagewithprivkey",
175 : : "signrawtransactionwithkey",
176 : : "submitblock",
177 : : "submitheader",
178 : : "submitpackage",
179 : : "syncwithvalidationinterfacequeue",
180 : : "testmempoolaccept",
181 : : "uptime",
182 : : "utxoupdatepsbt",
183 : : "validateaddress",
184 : : "verifychain",
185 : : "verifymessage",
186 : : "verifytxoutproof",
187 : : "waitforblock",
188 : : "waitforblockheight",
189 : : "waitfornewblock",
190 : : };
191 : :
192 : 158656 : UniValue ConsumeBasicRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
193 : : {
194 : 158656 : const size_t max_string_length = 4096;
195 : 158656 : const size_t max_base58_bytes_length{64};
196 [ + - ]: 158656 : UniValue r{};
197 [ + - ]: 158656 : CallOneOf(
198 : : fuzzed_data_provider,
199 : 4157 : [&] {
200 : : // arbitrary JSON argument
201 [ - + + - : 4157 : if (!r.read(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))) {
+ + ]
202 : 3753 : good_data = false;
203 : : }
204 : 4157 : },
205 : 30060 : [&] {
206 : : // null argument
207 : 30060 : r = UniValue{UniValue::VNULL};
208 : 30060 : },
209 : 2178 : [&] {
210 : : // string argument
211 [ + - ]: 2178 : r = UniValue{fuzzed_data_provider.ConsumeRandomLengthString(max_string_length)};
212 : 2178 : },
213 : 5442 : [&] {
214 : : // base64 argument
215 [ - + + - : 5442 : r = UniValue{EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))};
+ - ]
216 : 5442 : },
217 : 1908 : [&] {
218 : : // hex argument
219 [ - + + - : 1908 : r = UniValue{HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))};
+ - ]
220 : 1908 : },
221 : 7397 : [&] {
222 : : // bool argument
223 : 7397 : r = UniValue{fuzzed_data_provider.ConsumeBool()};
224 : 7397 : },
225 : 1690 : [&] {
226 : : // integral argument (int64_t)
227 : 1690 : r = UniValue{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
228 : 1690 : },
229 : 1457 : [&] {
230 : : // integral argument (uint64_t)
231 : 1457 : r = UniValue{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
232 : 1457 : },
233 : 8046 : [&] {
234 : : // floating point argument
235 : 8046 : r = UniValue{fuzzed_data_provider.ConsumeFloatingPoint<double>()};
236 : 8046 : },
237 : 9315 : [&] {
238 : : // tx destination argument
239 [ + - + - ]: 9315 : r = UniValue{EncodeDestination(ConsumeTxDestination(fuzzed_data_provider))};
240 : 9315 : },
241 : 2160 : [&] {
242 : : // uint160 argument
243 [ + - ]: 2160 : r = UniValue{ConsumeUInt160(fuzzed_data_provider).ToString()};
244 : 2160 : },
245 : 2912 : [&] {
246 : : // uint256 argument
247 [ + - ]: 2912 : r = UniValue{ConsumeUInt256(fuzzed_data_provider).ToString()};
248 : 2912 : },
249 : 2306 : [&] {
250 : : // base32 argument
251 [ - + + - : 2306 : r = UniValue{EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))};
+ - ]
252 : 2306 : },
253 : 52289 : [&] {
254 : : // base58 argument
255 [ + - + - ]: 52289 : r = UniValue{EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)))};
256 : 52289 : },
257 : 1887 : [&] {
258 : : // base58 argument with checksum
259 [ + - + - ]: 1887 : r = UniValue{EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)))};
260 : 1887 : },
261 : 674 : [&] {
262 : : // hex encoded block
263 : 674 : std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
264 [ + + ]: 674 : if (!opt_block) {
265 : 427 : good_data = false;
266 : 427 : return;
267 : : }
268 : 247 : DataStream data_stream{};
269 [ + - ]: 247 : data_stream << TX_WITH_WITNESS(*opt_block);
270 [ - + + - : 247 : r = UniValue{HexStr(data_stream)};
+ - ]
271 : 674 : },
272 : 1427 : [&] {
273 : : // hex encoded block header
274 : 1427 : std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
275 [ + + ]: 1427 : if (!opt_block_header) {
276 : 188 : good_data = false;
277 : 188 : return;
278 : : }
279 : 1239 : DataStream data_stream{};
280 [ + - ]: 1239 : data_stream << *opt_block_header;
281 [ - + + - : 1239 : r = UniValue{HexStr(data_stream)};
+ - ]
282 : 1239 : },
283 : 2317 : [&] {
284 : : // hex encoded tx
285 : 2317 : std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
286 [ + + ]: 2317 : if (!opt_tx) {
287 : 844 : good_data = false;
288 [ - + ]: 844 : return;
289 : : }
290 : 1473 : DataStream data_stream;
291 [ + + ]: 1473 : auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
292 [ + - ]: 1473 : data_stream << allow_witness(*opt_tx);
293 [ - + + - : 1473 : r = UniValue{HexStr(data_stream)};
+ - ]
294 [ + - ]: 3790 : },
295 : 15121 : [&] {
296 : : // base64 encoded psbt
297 : 15121 : std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializableConstructor<PartiallySignedTransaction>(fuzzed_data_provider);
298 [ + + ]: 15121 : if (!opt_psbt) {
299 : 1506 : good_data = false;
300 : 1506 : return;
301 : : }
302 : 13615 : DataStream data_stream{};
303 [ + - ]: 13615 : data_stream << *opt_psbt;
304 [ - + + - : 13615 : r = UniValue{EncodeBase64(data_stream)};
+ - ]
305 : 15121 : },
306 : 3522 : [&] {
307 : : // base58 encoded key
308 : 3522 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
309 [ + + ]: 3522 : if (!key.IsValid()) {
310 : 12 : good_data = false;
311 : 12 : return;
312 : : }
313 [ + - + - ]: 3510 : r = UniValue{EncodeSecret(key)};
314 : 3522 : },
315 : 2391 : [&] {
316 : : // hex encoded pubkey
317 : 2391 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
318 [ + + ]: 2391 : if (!key.IsValid()) {
319 : 7 : good_data = false;
320 : 7 : return;
321 : : }
322 [ + - + - : 2384 : r = UniValue{HexStr(key.GetPubKey())};
+ - ]
323 : 2391 : });
324 : 158656 : return r;
325 : 0 : }
326 : :
327 : : constexpr int MAX_RPC_ARGUMENT_NESTING{9};
328 : :
329 : : // NOLINTBEGIN(misc-no-recursion)
330 : 211542 : UniValue ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data, int nesting_depth)
331 : : {
332 [ + + ]: 211542 : if (nesting_depth == 0) {
333 : 113820 : return ConsumeBasicRPCArgument(fuzzed_data_provider, good_data);
334 : : }
335 [ + - ]: 97722 : UniValue argument{};
336 : 97722 : std::vector<std::function<void()>> mks{
337 : 44836 : [&] { argument = ConsumeBasicRPCArgument(fuzzed_data_provider, good_data); },
338 : 128229 : [&] {
339 : 30507 : argument = UniValue(UniValue::VARR);
340 [ + + + + : 159970 : LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 100) {
+ + ]
341 [ + - ]: 129463 : argument.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data, nesting_depth - 1));
342 : : }
343 : 30507 : },
344 : 120101 : [&] {
345 : 22379 : argument = UniValue(UniValue::VOBJ);
346 [ + + + + : 74923 : LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 100) {
+ + ]
347 [ + - + - ]: 52544 : argument.pushKV(fuzzed_data_provider.ConsumeRandomLengthString(128),
348 : 105088 : ConsumeRPCArgument(fuzzed_data_provider, good_data, nesting_depth - 1));
349 : : }
350 : 22379 : },
351 [ + - + + : 488610 : };
- - ]
352 [ + - ]: 97722 : PickValue(fuzzed_data_provider, mks)();
353 : 97722 : return argument;
354 [ + - + - : 195444 : }
+ - - - ]
355 : : // NOLINTEND(misc-no-recursion)
356 : :
357 : 1 : RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
358 : : {
359 [ + - + - : 1 : static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
+ - ]
360 : 1 : SetRPCWarmupFinished();
361 : 1 : return setup.get();
362 : : }
363 : : }; // namespace
364 : :
365 : 1 : void initialize_rpc()
366 : : {
367 : 1 : rpc_testing_setup = InitializeRPCFuzzTestingSetup();
368 : 1 : const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
369 [ + + ]: 116 : for (const std::string& rpc_command : supported_rpc_commands) {
370 : 115 : const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
371 [ - + ]: 115 : const bool not_safe_for_fuzzing = std::find(RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end();
372 [ - + ]: 115 : if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
373 [ # # # # : 0 : std::cerr << "Error: RPC command \"" << rpc_command << "\" not found in RPC_COMMANDS_SAFE_FOR_FUZZING or RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
# # # # #
# ]
374 : 0 : std::terminate();
375 : : }
376 [ - + ]: 115 : if (safe_for_fuzzing && not_safe_for_fuzzing) {
377 [ # # # # : 0 : std::cerr << "Error: RPC command \"" << rpc_command << "\" found in *both* RPC_COMMANDS_SAFE_FOR_FUZZING and RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
# # # # #
# ]
378 : 0 : std::terminate();
379 : : }
380 : : }
381 : 1 : const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
382 [ - + ]: 1 : if (limit_to_rpc_command_env != nullptr) {
383 [ # # ]: 0 : g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
384 : : }
385 : 1 : }
386 : :
387 [ + - ]: 20251 : FUZZ_TARGET(rpc, .init = initialize_rpc)
388 : : {
389 : 19775 : SeedRandomStateForTest(SeedRand::ZEROS);
390 : 19775 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
391 : 19775 : bool good_data{true};
392 : 19775 : FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
393 [ + - ]: 19775 : const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
394 [ - + - - ]: 19775 : if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
395 : : return;
396 : : }
397 [ + + ]: 19775 : const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
398 [ + + ]: 19775 : if (!safe_for_fuzzing) {
399 : : return;
400 : : }
401 : 19761 : UniValue arguments(UniValue::VARR);
402 [ + + + + : 91855 : LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 100) {
+ + ]
403 [ + - + - ]: 29535 : arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data, MAX_RPC_ARGUMENT_NESTING));
404 : : }
405 : 19761 : try {
406 : 19761 : std::optional<test_only_CheckFailuresAreExceptionsNotAborts> maybe_mock{};
407 [ + + ]: 19761 : if (rpc_command == "echo") {
408 : : // Avoid aborting fuzzing for this specific test-only RPC with an
409 : : // intentional trigger_internal_bug
410 : 276 : maybe_mock.emplace();
411 : : }
412 [ + + ]: 19761 : rpc_testing_setup->CallRPC(rpc_command, std::move(arguments));
413 [ - + ]: 19761 : } catch (const UniValue& json_rpc_error) {
414 [ + - + - : 15750 : const std::string error_msg{json_rpc_error.find_value("message").get_str()};
- + ]
415 [ - + - + ]: 15750 : if (error_msg.starts_with("Internal bug detected")) {
416 : : // Only allow the intentional internal bug
417 [ - - ]: 0 : assert(error_msg.find("trigger_internal_bug") != std::string::npos);
418 : : }
419 : 15750 : }
420 : 19775 : }
|