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 : 13383 : void CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
50 : : {
51 : 13383 : JSONRPCRequest request;
52 : 13383 : request.context = &m_node;
53 [ + - ]: 13383 : request.strMethod = rpc_method;
54 : 13383 : try {
55 [ + + ]: 13383 : request.params = RPCConvertValues(rpc_method, arguments);
56 [ - + ]: 747 : } catch (const std::runtime_error&) {
57 : 747 : return;
58 : 747 : }
59 [ + + ]: 12636 : tableRPC.execute(request);
60 : 13383 : }
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 : : "exportasmap", // avoid writing to disk
82 : : "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
83 : : "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
84 : : "gettxoutproof", // avoid prohibitively slow execution
85 : : "importmempool", // avoid reading from disk
86 : : "loadtxoutset", // avoid reading from disk
87 : : "loadwallet", // avoid reading from disk
88 : : "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
89 : : "setban", // avoid DNS lookups
90 : : "stop", // avoid shutdown state
91 : : };
92 : :
93 : : // RPC commands which are safe for fuzzing.
94 : : const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
95 : : "abortprivatebroadcast",
96 : : "analyzepsbt",
97 : : "clearbanned",
98 : : "combinepsbt",
99 : : "combinerawtransaction",
100 : : "converttopsbt",
101 : : "createmultisig",
102 : : "createpsbt",
103 : : "createrawtransaction",
104 : : "decodepsbt",
105 : : "decoderawtransaction",
106 : : "decodescript",
107 : : "deriveaddresses",
108 : : "descriptorprocesspsbt",
109 : : "disconnectnode",
110 : : "echo",
111 : : "echojson",
112 : : "estimaterawfee",
113 : : "estimatesmartfee",
114 : : "finalizepsbt",
115 : : "generate",
116 : : "generateblock",
117 : : "getaddednodeinfo",
118 : : "getaddrmaninfo",
119 : : "getbestblockhash",
120 : : "getblock",
121 : : "getblockchaininfo",
122 : : "getblockcount",
123 : : "getblockfilter",
124 : : "getblockfrompeer", // when no peers are connected, no p2p message is sent
125 : : "getblockhash",
126 : : "getblockheader",
127 : : "getblockstats",
128 : : "getblocktemplate",
129 : : "getchaintips",
130 : : "getchainstates",
131 : : "getchaintxstats",
132 : : "getconnectioncount",
133 : : "getdeploymentinfo",
134 : : "getdescriptoractivity",
135 : : "getdescriptorinfo",
136 : : "getdifficulty",
137 : : "getindexinfo",
138 : : "getmemoryinfo",
139 : : "getmempoolancestors",
140 : : "getmempooldescendants",
141 : : "getmempoolentry",
142 : : "getmempoolfeeratediagram",
143 : : "getmempoolcluster",
144 : : "getmempoolinfo",
145 : : "getmininginfo",
146 : : "getnettotals",
147 : : "getnetworkhashps",
148 : : "getnetworkinfo",
149 : : "getnodeaddresses",
150 : : "getorphantxs",
151 : : "getpeerinfo",
152 : : "getprioritisedtransactions",
153 : : "getprivatebroadcastinfo",
154 : : "getrawaddrman",
155 : : "getrawmempool",
156 : : "getrawtransaction",
157 : : "getrpcinfo",
158 : : "gettxout",
159 : : "gettxoutsetinfo",
160 : : "gettxspendingprevout",
161 : : "help",
162 : : "invalidateblock",
163 : : "joinpsbts",
164 : : "listbanned",
165 : : "logging",
166 : : "mockscheduler",
167 : : "ping",
168 : : "preciousblock",
169 : : "prioritisetransaction",
170 : : "pruneblockchain",
171 : : "reconsiderblock",
172 : : "scanblocks",
173 : : "scantxoutset",
174 : : "sendmsgtopeer", // when no peers are connected, no p2p message is sent
175 : : "sendrawtransaction",
176 : : "setmocktime",
177 : : "setnetworkactive",
178 : : "signmessagewithprivkey",
179 : : "signrawtransactionwithkey",
180 : : "submitblock",
181 : : "submitheader",
182 : : "submitpackage",
183 : : "syncwithvalidationinterfacequeue",
184 : : "testmempoolaccept",
185 : : "uptime",
186 : : "utxoupdatepsbt",
187 : : "validateaddress",
188 : : "verifychain",
189 : : "verifymessage",
190 : : "verifytxoutproof",
191 : : "waitforblock",
192 : : "waitforblockheight",
193 : : "waitfornewblock",
194 : : };
195 : :
196 : 218799 : std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
197 : : {
198 : 218799 : const size_t max_string_length = 4096;
199 : 218799 : const size_t max_base58_bytes_length{64};
200 [ + - ]: 218799 : std::string r;
201 [ + - ]: 218799 : CallOneOf(
202 : : fuzzed_data_provider,
203 : 10422 : [&] {
204 : : // string argument
205 : 10422 : r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
206 : 10422 : },
207 : 6968 : [&] {
208 : : // base64 argument
209 [ - + + - ]: 6968 : r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
210 : 6968 : },
211 : 3780 : [&] {
212 : : // hex argument
213 [ - + + - ]: 3780 : r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
214 : 3780 : },
215 : 16207 : [&] {
216 : : // bool argument
217 [ + + ]: 20251 : r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
218 : 16207 : },
219 : 8657 : [&] {
220 : : // range argument
221 [ + - + - : 25971 : r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
+ - ]
222 : 8657 : },
223 : 5968 : [&] {
224 : : // integral argument (int64_t)
225 : 5968 : r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
226 : 5968 : },
227 : 4950 : [&] {
228 : : // integral argument (uint64_t)
229 : 4950 : r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
230 : 4950 : },
231 : 22841 : [&] {
232 : : // floating point argument
233 : 22841 : r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
234 : 22841 : },
235 : 22518 : [&] {
236 : : // tx destination argument
237 [ + - ]: 22518 : r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
238 : 22518 : },
239 : 2364 : [&] {
240 : : // uint160 argument
241 : 2364 : r = ConsumeUInt160(fuzzed_data_provider).ToString();
242 : 2364 : },
243 : 3409 : [&] {
244 : : // uint256 argument
245 : 3409 : r = ConsumeUInt256(fuzzed_data_provider).ToString();
246 : 3409 : },
247 : 7139 : [&] {
248 : : // base32 argument
249 [ - + + - ]: 7139 : r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
250 : 7139 : },
251 : 15459 : [&] {
252 : : // base58 argument
253 [ + - ]: 15459 : r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
254 : 15459 : },
255 : 8617 : [&] {
256 : : // base58 argument with checksum
257 [ + - ]: 8617 : r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
258 : 8617 : },
259 : 2126 : [&] {
260 : : // hex encoded block
261 : 2126 : std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
262 [ + + ]: 2126 : if (!opt_block) {
263 : 95 : good_data = false;
264 : 95 : return;
265 : : }
266 : 2031 : DataStream data_stream{};
267 [ + - ]: 2031 : data_stream << TX_WITH_WITNESS(*opt_block);
268 [ - + + - ]: 2031 : r = HexStr(data_stream);
269 : 2126 : },
270 : 946 : [&] {
271 : : // hex encoded block header
272 : 946 : std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
273 [ + + ]: 946 : if (!opt_block_header) {
274 : 58 : good_data = false;
275 : 58 : return;
276 : : }
277 : 888 : DataStream data_stream{};
278 [ + - ]: 888 : data_stream << *opt_block_header;
279 [ - + + - ]: 888 : r = HexStr(data_stream);
280 : 888 : },
281 : 6290 : [&] {
282 : : // hex encoded tx
283 : 6290 : std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
284 [ + + ]: 6290 : if (!opt_tx) {
285 : 108 : good_data = false;
286 [ - + ]: 108 : return;
287 : : }
288 : 6182 : DataStream data_stream;
289 [ + + ]: 6182 : auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
290 [ + - ]: 6182 : data_stream << allow_witness(*opt_tx);
291 [ - + + - ]: 6182 : r = HexStr(data_stream);
292 [ + - ]: 12472 : },
293 : 50262 : [&] {
294 : : // base64 encoded psbt
295 : 50262 : std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
296 [ + + ]: 50262 : if (!opt_psbt) {
297 : 1294 : good_data = false;
298 : 1294 : return;
299 : : }
300 : 48968 : DataStream data_stream{};
301 [ + - ]: 48968 : data_stream << *opt_psbt;
302 [ - + + - ]: 48968 : r = EncodeBase64(data_stream);
303 : 50262 : },
304 : 8366 : [&] {
305 : : // base58 encoded key
306 : 8366 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
307 [ + + ]: 8366 : if (!key.IsValid()) {
308 : 6 : good_data = false;
309 : 6 : return;
310 : : }
311 [ + - ]: 8360 : r = EncodeSecret(key);
312 : 8366 : },
313 : 11510 : [&] {
314 : : // hex encoded pubkey
315 : 11510 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
316 [ + + ]: 11510 : if (!key.IsValid()) {
317 : 30 : good_data = false;
318 : 30 : return;
319 : : }
320 [ + - + - ]: 11480 : r = HexStr(key.GetPubKey());
321 : 11510 : });
322 : 218799 : return r;
323 : 0 : }
324 : :
325 : 13644 : std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
326 : : {
327 : 13644 : std::vector<std::string> scalar_arguments;
328 [ + + + + : 416948 : LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
+ + ]
329 : : {
330 [ + - ]: 390728 : scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
331 : : }
332 [ + - + - ]: 40932 : return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
333 : 13644 : }
334 : :
335 : 37079 : std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
336 : : {
337 [ + + ]: 37079 : return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
338 : : }
339 : :
340 : 1 : RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
341 : : {
342 [ + - + - : 1 : static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
+ - ]
343 : 1 : SetRPCWarmupFinished();
344 : 1 : return setup.get();
345 : : }
346 : : }; // namespace
347 : :
348 : 1 : void initialize_rpc()
349 : : {
350 : 1 : rpc_testing_setup = InitializeRPCFuzzTestingSetup();
351 : 1 : const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
352 [ + + ]: 114 : for (const std::string& rpc_command : supported_rpc_commands) {
353 : 113 : 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();
354 [ - + ]: 113 : 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();
355 [ - + ]: 113 : if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
356 [ # # # # : 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";
# # # # #
# ]
357 : 0 : std::terminate();
358 : : }
359 [ - + ]: 113 : if (safe_for_fuzzing && not_safe_for_fuzzing) {
360 [ # # # # : 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";
# # # # #
# ]
361 : 0 : std::terminate();
362 : : }
363 : : }
364 : 1 : const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
365 [ - + ]: 1 : if (limit_to_rpc_command_env != nullptr) {
366 [ # # ]: 0 : g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
367 : : }
368 : 1 : }
369 : :
370 [ + - ]: 13848 : FUZZ_TARGET(rpc, .init = initialize_rpc)
371 : : {
372 : 13390 : SeedRandomStateForTest(SeedRand::ZEROS);
373 : 13390 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
374 : 13390 : bool good_data{true};
375 : 13390 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)};
376 [ + - ]: 13390 : const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
377 [ - + - - ]: 13390 : if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
378 : : return;
379 : : }
380 [ + + ]: 13390 : 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();
381 [ + + ]: 13390 : if (!safe_for_fuzzing) {
382 : : return;
383 : : }
384 : 13383 : std::vector<std::string> arguments;
385 [ + + + + : 99333 : LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
+ + ]
386 : : {
387 [ + - ]: 74158 : arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
388 : : }
389 : 13383 : try {
390 : 13383 : std::optional<test_only_CheckFailuresAreExceptionsNotAborts> maybe_mock{};
391 [ + + ]: 13383 : if (rpc_command == "echo") {
392 : : // Avoid aborting fuzzing for this specific test-only RPC with an
393 : : // intentional trigger_internal_bug
394 : 92 : maybe_mock.emplace();
395 : : }
396 [ + + ]: 13383 : rpc_testing_setup->CallRPC(rpc_command, arguments);
397 [ - + ]: 13383 : } catch (const UniValue& json_rpc_error) {
398 [ + - + - : 4503 : const std::string error_msg{json_rpc_error.find_value("message").get_str()};
- + ]
399 [ - + + + ]: 4503 : if (error_msg.starts_with("Internal bug detected")) {
400 : : // Only allow the intentional internal bug
401 [ - + ]: 1 : assert(error_msg.find("trigger_internal_bug") != std::string::npos);
402 : : }
403 : 4503 : }
404 : 13390 : }
|