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