Branch data Line data Source code
1 : : // Copyright (c) 2021-2022 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 <core_io.h>
7 : : #include <key.h>
8 : : #include <key_io.h>
9 : : #include <node/context.h>
10 : : #include <primitives/block.h>
11 : : #include <primitives/transaction.h>
12 : : #include <psbt.h>
13 : : #include <rpc/blockchain.h>
14 : : #include <rpc/client.h>
15 : : #include <rpc/request.h>
16 : : #include <rpc/server.h>
17 [ + - ]: 173 : #include <rpc/util.h>
18 [ + - ]: 173 : #include <span.h>
19 : : #include <streams.h>
20 : : #include <test/fuzz/FuzzedDataProvider.h>
21 : : #include <test/fuzz/fuzz.h>
22 : : #include <test/fuzz/util.h>
23 : : #include <test/util/setup_common.h>
24 : : #include <tinyformat.h>
25 : : #include <univalue.h>
26 : : #include <util/chaintype.h>
27 : 173 : #include <util/strencodings.h>
28 : : #include <util/string.h>
29 : : #include <util/time.h>
30 : :
31 : : #include <cstdint>
32 : : #include <iostream>
33 : : #include <memory>
34 : : #include <optional>
35 : : #include <stdexcept>
36 : : #include <string>
37 : : #include <vector>
38 : :
39 : : namespace {
40 : : struct RPCFuzzTestingSetup : public TestingSetup {
41 : 1 : RPCFuzzTestingSetup(const ChainType chain_type, const std::vector<const char*>& extra_args) : TestingSetup{chain_type, extra_args}
42 : : {
43 : 1 : }
44 : :
45 : 4659 : void CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
46 : : {
47 : 4659 : JSONRPCRequest request;
48 [ + - ]: 4659 : request.context = &m_node;
49 [ + - ]: 4659 : request.strMethod = rpc_method;
50 : : try {
51 [ + + ]: 4659 : request.params = RPCConvertValues(rpc_method, arguments);
52 [ + - ]: 4659 : } catch (const std::runtime_error&) {
53 : : return;
54 [ + - ]: 922 : }
55 [ + + ]: 3737 : tableRPC.execute(request);
56 [ - + ]: 5581 : }
57 : :
58 : 1 : std::vector<std::string> GetRPCCommands() const
59 : : {
60 : 1 : return tableRPC.listCommands();
61 : : }
62 : : };
63 : :
64 : : RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
65 : 173 : std::string g_limit_to_rpc_command;
66 : :
67 : : // RPC commands which are not appropriate for fuzzing: such as RPC commands
68 : : // reading or writing to a filename passed as an RPC parameter, RPC commands
69 : : // resulting in network activity, etc.
70 [ + - # # ]: 173 : const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
71 [ + - ]: 173 : "addconnection", // avoid DNS lookups
72 [ + - ]: 173 : "addnode", // avoid DNS lookups
73 [ + - ]: 173 : "addpeeraddress", // avoid DNS lookups
74 [ + - ]: 346 : "dumptxoutset", // avoid writing to disk
75 [ + - ]: 173 : "dumpwallet", // avoid writing to disk
76 [ + - ]: 173 : "enumeratesigners",
77 [ + - ]: 173 : "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
78 [ + - ]: 173 : "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
79 [ + - ]: 173 : "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
80 [ + - ]: 173 : "gettxoutproof", // avoid prohibitively slow execution
81 [ + - ]: 173 : "importmempool", // avoid reading from disk
82 [ + - ]: 173 : "importwallet", // avoid reading from disk
83 [ + - ]: 173 : "loadtxoutset", // avoid reading from disk
84 [ + - ]: 173 : "loadwallet", // avoid reading from disk
85 [ + - ]: 173 : "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
86 [ + - ]: 173 : "setban", // avoid DNS lookups
87 [ + - ]: 173 : "stop", // avoid shutdown state
88 : : };
89 : :
90 : : // RPC commands which are safe for fuzzing.
91 [ + - # # ]: 173 : const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
92 [ + - ]: 173 : "analyzepsbt",
93 [ + - ]: 173 : "clearbanned",
94 [ + - ]: 173 : "combinepsbt",
95 [ + - ]: 173 : "combinerawtransaction",
96 [ + - ]: 173 : "converttopsbt",
97 [ + - ]: 173 : "createmultisig",
98 [ + - ]: 173 : "createpsbt",
99 [ + - ]: 173 : "createrawtransaction",
100 [ + - ]: 173 : "decodepsbt",
101 [ + - ]: 173 : "decoderawtransaction",
102 [ + - ]: 173 : "decodescript",
103 [ + - ]: 173 : "deriveaddresses",
104 [ + - ]: 173 : "descriptorprocesspsbt",
105 [ + - ]: 173 : "disconnectnode",
106 [ + - ]: 173 : "echo",
107 [ + - ]: 173 : "echojson",
108 [ + - ]: 173 : "estimaterawfee",
109 [ + - ]: 173 : "estimatesmartfee",
110 [ + - ]: 173 : "finalizepsbt",
111 [ + - ]: 173 : "generate",
112 [ + - ]: 173 : "generateblock",
113 [ + - ]: 173 : "getaddednodeinfo",
114 [ + - ]: 173 : "getaddrmaninfo",
115 [ + - ]: 173 : "getbestblockhash",
116 [ + - ]: 173 : "getblock",
117 [ + - ]: 173 : "getblockchaininfo",
118 [ + - ]: 173 : "getblockcount",
119 [ + - ]: 173 : "getblockfilter",
120 [ + - ]: 173 : "getblockfrompeer", // when no peers are connected, no p2p message is sent
121 [ + - ]: 173 : "getblockhash",
122 [ + - ]: 173 : "getblockheader",
123 [ + - ]: 173 : "getblockstats",
124 [ + - ]: 173 : "getblocktemplate",
125 [ + - ]: 173 : "getchaintips",
126 [ + - ]: 173 : "getchainstates",
127 [ + - ]: 173 : "getchaintxstats",
128 [ + - ]: 173 : "getconnectioncount",
129 [ + - ]: 173 : "getdeploymentinfo",
130 [ + - ]: 173 : "getdescriptorinfo",
131 [ + - ]: 173 : "getdifficulty",
132 [ + - ]: 173 : "getindexinfo",
133 [ + - ]: 173 : "getmemoryinfo",
134 [ + - ]: 173 : "getmempoolancestors",
135 [ + - ]: 173 : "getmempooldescendants",
136 [ + - ]: 173 : "getmempoolentry",
137 [ + - ]: 173 : "getmempoolinfo",
138 [ + - ]: 173 : "getmininginfo",
139 [ + - ]: 173 : "getnettotals",
140 [ + - ]: 173 : "getnetworkhashps",
141 [ + - ]: 173 : "getnetworkinfo",
142 [ + - ]: 173 : "getnodeaddresses",
143 [ + - ]: 173 : "getpeerinfo",
144 [ + - ]: 173 : "getprioritisedtransactions",
145 [ + - ]: 173 : "getrawaddrman",
146 [ + - ]: 173 : "getrawmempool",
147 [ + - ]: 173 : "getrawtransaction",
148 [ + - ]: 173 : "getrpcinfo",
149 [ + - ]: 173 : "gettxout",
150 [ + - ]: 173 : "gettxoutsetinfo",
151 [ + - ]: 173 : "gettxspendingprevout",
152 [ + - ]: 173 : "help",
153 [ + - ]: 173 : "invalidateblock",
154 [ + - ]: 173 : "joinpsbts",
155 [ + - ]: 173 : "listbanned",
156 [ + - ]: 173 : "logging",
157 [ + - ]: 173 : "mockscheduler",
158 [ + - ]: 173 : "ping",
159 [ + - ]: 173 : "preciousblock",
160 [ + - ]: 173 : "prioritisetransaction",
161 [ + - ]: 173 : "pruneblockchain",
162 [ + - ]: 173 : "reconsiderblock",
163 [ + - ]: 173 : "scanblocks",
164 [ + - ]: 173 : "scantxoutset",
165 [ + - ]: 173 : "sendmsgtopeer", // when no peers are connected, no p2p message is sent
166 [ + - ]: 173 : "sendrawtransaction",
167 [ + - ]: 173 : "setmocktime",
168 [ + - ]: 173 : "setnetworkactive",
169 [ + - ]: 173 : "signmessagewithprivkey",
170 [ + - ]: 173 : "signrawtransactionwithkey",
171 [ + - ]: 173 : "submitblock",
172 [ + - ]: 173 : "submitheader",
173 [ + - ]: 173 : "submitpackage",
174 [ + - ]: 173 : "syncwithvalidationinterfacequeue",
175 [ + - ]: 173 : "testmempoolaccept",
176 [ + - ]: 173 : "uptime",
177 [ + - ]: 173 : "utxoupdatepsbt",
178 [ + - ]: 173 : "validateaddress",
179 [ + - ]: 173 : "verifychain",
180 [ + - ]: 173 : "verifymessage",
181 [ + - ]: 173 : "verifytxoutproof",
182 [ + - ]: 173 : "waitforblock",
183 [ + - ]: 173 : "waitforblockheight",
184 [ + - ]: 173 : "waitfornewblock",
185 : : };
186 : :
187 : 227740 : std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
188 : : {
189 : 227740 : const size_t max_string_length = 4096;
190 : 227740 : const size_t max_base58_bytes_length{64};
191 : 227740 : std::string r;
192 [ + - ]: 227740 : CallOneOf(
193 : 227740 : fuzzed_data_provider,
194 : 230235 : [&] {
195 : : // string argument
196 : 2495 : r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
197 : 2495 : },
198 : 231171 : [&] {
199 : : // base64 argument
200 [ + - ]: 3431 : r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
201 : 3431 : },
202 : 229211 : [&] {
203 : : // hex argument
204 [ + - + - ]: 1471 : r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
205 : 1471 : },
206 : 236004 : [&] {
207 : : // bool argument
208 : 8264 : r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
209 : 8264 : },
210 : 228526 : [&] {
211 : : // range argument
212 [ + - + - : 786 : r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
+ - + - +
- - + ]
213 : 786 : },
214 : 231499 : [&] {
215 : : // integral argument (int64_t)
216 : 3759 : r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
217 : 3759 : },
218 : 228570 : [&] {
219 : : // integral argument (uint64_t)
220 : 830 : r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
221 : 830 : },
222 : 229418 : [&] {
223 : : // floating point argument
224 : 1678 : r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
225 : 1678 : },
226 : 230114 : [&] {
227 : : // tx destination argument
228 [ + - ]: 2374 : r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
229 : 2374 : },
230 : 228342 : [&] {
231 : : // uint160 argument
232 : 602 : r = ConsumeUInt160(fuzzed_data_provider).ToString();
233 : 602 : },
234 : 232573 : [&] {
235 : : // uint256 argument
236 : 4833 : r = ConsumeUInt256(fuzzed_data_provider).ToString();
237 : 4833 : },
238 : 233171 : [&] {
239 : : // base32 argument
240 [ + - ]: 5431 : r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
241 : 5431 : },
242 : 229004 : [&] {
243 : : // base58 argument
244 [ + - + - ]: 1264 : r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
245 : 1264 : },
246 : 229250 : [&] {
247 : : // base58 argument with checksum
248 [ + - + - ]: 1510 : r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
249 : 1510 : },
250 : 235112 : [&] {
251 : : // hex encoded block
252 : 7372 : std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
253 [ + + ]: 7372 : if (!opt_block) {
254 : 6039 : return;
255 : : }
256 [ + - ]: 1333 : CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
257 [ + - + - ]: 1333 : data_stream << *opt_block;
258 [ + - + - ]: 1333 : r = HexStr(data_stream);
259 [ - + ]: 7372 : },
260 : 251186 : [&] {
261 : : // hex encoded block header
262 : 23446 : std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
263 [ + + ]: 23446 : if (!opt_block_header) {
264 : 17451 : return;
265 : : }
266 : 5995 : DataStream data_stream{};
267 [ + - + - ]: 5995 : data_stream << *opt_block_header;
268 [ + - + - ]: 5995 : r = HexStr(data_stream);
269 : 23446 : },
270 : 233090 : [&] {
271 : : // hex encoded tx
272 : 5350 : std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
273 [ + + ]: 5350 : if (!opt_tx) {
274 : 3250 : return;
275 : : }
276 [ + - + - ]: 2100 : CDataStream data_stream{SER_NETWORK, fuzzed_data_provider.ConsumeBool() ? PROTOCOL_VERSION : (PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)};
277 [ + - + - ]: 2100 : data_stream << *opt_tx;
278 [ + - + - ]: 2100 : r = HexStr(data_stream);
279 [ - + ]: 5350 : },
280 : 370574 : [&] {
281 : : // base64 encoded psbt
282 : 142834 : std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
283 [ + + ]: 142834 : if (!opt_psbt) {
284 : 112319 : return;
285 : : }
286 [ + - ]: 30515 : CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
287 [ + - + - ]: 30515 : data_stream << *opt_psbt;
288 [ + - + - ]: 30515 : r = EncodeBase64(data_stream);
289 [ - + ]: 142834 : },
290 : 236765 : [&] {
291 : : // base58 encoded key
292 : 9025 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
293 [ + - + + ]: 9025 : if (!key.IsValid()) {
294 : 386 : return;
295 : : }
296 [ + - ]: 8639 : r = EncodeSecret(key);
297 [ - + ]: 9025 : },
298 : 228725 : [&] {
299 : : // hex encoded pubkey
300 : 985 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
301 [ + + ]: 985 : if (!key.IsValid()) {
302 : 61 : return;
303 : : }
304 [ + - + - : 924 : r = HexStr(key.GetPubKey());
+ - ]
305 [ - + ]: 985 : });
306 : 227740 : return r;
307 [ + - ]: 227740 : }
308 : :
309 : 7221 : std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
310 : : {
311 : 7221 : std::vector<std::string> scalar_arguments;
312 [ + - + + : 192867 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
+ + ]
313 [ + - + - ]: 185646 : scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
314 : 185646 : }
315 [ + - + - : 7221 : return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
- + ]
316 : 7221 : }
317 : :
318 : 49315 : std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
319 : : {
320 [ + + ]: 49315 : return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
321 : : }
322 : :
323 : 1 : RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
324 : : {
325 [ + - - + : 1 : static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
+ - ]
326 : 1 : SetRPCWarmupFinished();
327 : 1 : return setup.get();
328 : 0 : }
329 : : }; // namespace
330 : :
331 : 1 : void initialize_rpc()
332 : : {
333 : 1 : rpc_testing_setup = InitializeRPCFuzzTestingSetup();
334 : 1 : const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
335 [ + + ]: 107 : for (const std::string& rpc_command : supported_rpc_commands) {
336 [ + - ]: 106 : 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();
337 [ + - ]: 106 : 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();
338 [ + + - + ]: 106 : if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
339 [ # # # # : 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";
# # # # #
# ]
340 : 0 : std::terminate();
341 : : }
342 [ + + - + ]: 106 : if (safe_for_fuzzing && not_safe_for_fuzzing) {
343 [ # # # # : 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";
# # # # #
# ]
344 : 0 : std::terminate();
345 : : }
346 : : }
347 : 1 : const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
348 [ + - ]: 1 : if (limit_to_rpc_command_env != nullptr) {
349 [ # # ]: 0 : g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
350 : 0 : }
351 : 1 : }
352 : :
353 [ + - - + ]: 5008 : FUZZ_TARGET(rpc, .init = initialize_rpc)
354 : : {
355 : 4662 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
356 : 4662 : SetMockTime(ConsumeTime(fuzzed_data_provider));
357 : 4662 : const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
358 [ - + # # ]: 4662 : if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
359 : 0 : return;
360 : : }
361 [ + - ]: 4662 : 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();
362 [ + + ]: 4662 : if (!safe_for_fuzzing) {
363 : 3 : return;
364 : : }
365 : 4659 : std::vector<std::string> arguments;
366 [ + + + + ]: 53974 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
367 [ + + + - ]: 44726 : arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
368 : 49315 : }
369 : : try {
370 [ + + ]: 4659 : rpc_testing_setup->CallRPC(rpc_command, arguments);
371 [ - + ]: 4659 : } catch (const UniValue& json_rpc_error) {
372 [ + - + - : 1748 : const std::string error_msg{json_rpc_error.find_value("message").get_str()};
+ - ]
373 : : // Once c++20 is allowed, starts_with can be used.
374 : : // if (error_msg.starts_with("Internal bug detected")) {
375 [ + - + + ]: 1748 : if (0 == error_msg.rfind("Internal bug detected", 0)) {
376 : : // Only allow the intentional internal bug
377 [ + - ]: 1 : assert(error_msg.find("trigger_internal_bug") != std::string::npos);
378 : 1 : }
379 [ + - # # ]: 1748 : }
380 [ - + ]: 15588 : }
|