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 <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 <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 <vector>
37 : : enum class ChainType;
38 : :
39 : : using util::Join;
40 : : using util::ToString;
41 : :
42 : : namespace {
43 : 1 : struct RPCFuzzTestingSetup : public TestingSetup {
44 [ + - ]: 1 : RPCFuzzTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
45 : : {
46 : 1 : }
47 : :
48 : 7928 : void CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
49 : : {
50 : 7928 : JSONRPCRequest request;
51 : 7928 : request.context = &m_node;
52 [ + - ]: 7928 : request.strMethod = rpc_method;
53 : 7928 : try {
54 [ + + ]: 7928 : request.params = RPCConvertValues(rpc_method, arguments);
55 [ - + ]: 489 : } catch (const std::runtime_error&) {
56 : 489 : return;
57 : 489 : }
58 [ + + ]: 7439 : tableRPC.execute(request);
59 : 7928 : }
60 : :
61 : 1 : std::vector<std::string> GetRPCCommands() const
62 : : {
63 : 1 : return tableRPC.listCommands();
64 : : }
65 : : };
66 : :
67 : : RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
68 : : std::string g_limit_to_rpc_command;
69 : :
70 : : // RPC commands which are not appropriate for fuzzing: such as RPC commands
71 : : // reading or writing to a filename passed as an RPC parameter, RPC commands
72 : : // resulting in network activity, etc.
73 : : const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
74 : : "addconnection", // avoid DNS lookups
75 : : "addnode", // avoid DNS lookups
76 : : "addpeeraddress", // avoid DNS lookups
77 : : "dumptxoutset", // avoid writing to disk
78 : : "dumpwallet", // 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 : : "importwallet", // 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 : : "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 : : "getmempoolinfo",
142 : : "getmininginfo",
143 : : "getnettotals",
144 : : "getnetworkhashps",
145 : : "getnetworkinfo",
146 : : "getnodeaddresses",
147 : : "getorphantxs",
148 : : "getpeerinfo",
149 : : "getprioritisedtransactions",
150 : : "getrawaddrman",
151 : : "getrawmempool",
152 : : "getrawtransaction",
153 : : "getrpcinfo",
154 : : "gettxout",
155 : : "gettxoutsetinfo",
156 : : "gettxspendingprevout",
157 : : "help",
158 : : "invalidateblock",
159 : : "joinpsbts",
160 : : "listbanned",
161 : : "logging",
162 : : "mockscheduler",
163 : : "ping",
164 : : "preciousblock",
165 : : "prioritisetransaction",
166 : : "pruneblockchain",
167 : : "reconsiderblock",
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 : 157507 : std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
193 : : {
194 : 157507 : const size_t max_string_length = 4096;
195 : 157507 : const size_t max_base58_bytes_length{64};
196 [ + - ]: 157507 : std::string r;
197 [ + - ]: 157507 : CallOneOf(
198 : : fuzzed_data_provider,
199 : 4229 : [&] {
200 : : // string argument
201 : 4229 : r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
202 : 4229 : },
203 : 5269 : [&] {
204 : : // base64 argument
205 [ + - ]: 5269 : r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
206 : 5269 : },
207 : 2928 : [&] {
208 : : // hex argument
209 [ + - ]: 2928 : r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
210 : 2928 : },
211 : 13528 : [&] {
212 : : // bool argument
213 [ + + ]: 16330 : r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
214 : 13528 : },
215 : 5518 : [&] {
216 : : // range argument
217 [ + - + - : 16554 : r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
+ - ]
218 : 5518 : },
219 : 4669 : [&] {
220 : : // integral argument (int64_t)
221 : 4669 : r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
222 : 4669 : },
223 : 2870 : [&] {
224 : : // integral argument (uint64_t)
225 : 2870 : r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
226 : 2870 : },
227 : 15583 : [&] {
228 : : // floating point argument
229 : 15583 : r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
230 : 15583 : },
231 : 14816 : [&] {
232 : : // tx destination argument
233 [ + - ]: 14816 : r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
234 : 14816 : },
235 : 1743 : [&] {
236 : : // uint160 argument
237 : 1743 : r = ConsumeUInt160(fuzzed_data_provider).ToString();
238 : 1743 : },
239 : 2262 : [&] {
240 : : // uint256 argument
241 : 2262 : r = ConsumeUInt256(fuzzed_data_provider).ToString();
242 : 2262 : },
243 : 6840 : [&] {
244 : : // base32 argument
245 [ + - ]: 6840 : r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
246 : 6840 : },
247 : 11165 : [&] {
248 : : // base58 argument
249 [ + - ]: 11165 : r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
250 : 11165 : },
251 : 6677 : [&] {
252 : : // base58 argument with checksum
253 [ + - ]: 6677 : r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
254 : 6677 : },
255 : 2512 : [&] {
256 : : // hex encoded block
257 : 2512 : std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
258 [ + + ]: 2512 : if (!opt_block) {
259 : 60 : good_data = false;
260 : 60 : return;
261 : : }
262 : 2452 : DataStream data_stream{};
263 [ + - ]: 2452 : data_stream << TX_WITH_WITNESS(*opt_block);
264 [ + - ]: 2452 : r = HexStr(data_stream);
265 : 2512 : },
266 : 1153 : [&] {
267 : : // hex encoded block header
268 : 1153 : std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
269 [ + + ]: 1153 : if (!opt_block_header) {
270 : 40 : good_data = false;
271 : 40 : return;
272 : : }
273 : 1113 : DataStream data_stream{};
274 [ + - ]: 1113 : data_stream << *opt_block_header;
275 [ + - ]: 1113 : r = HexStr(data_stream);
276 : 1113 : },
277 : 4590 : [&] {
278 : : // hex encoded tx
279 : 4590 : std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
280 [ + + ]: 4590 : if (!opt_tx) {
281 : 84 : good_data = false;
282 [ - + ]: 84 : return;
283 : : }
284 : 4506 : DataStream data_stream;
285 [ + + ]: 4506 : auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
286 [ + - ]: 4506 : data_stream << allow_witness(*opt_tx);
287 [ + - ]: 4506 : r = HexStr(data_stream);
288 [ + - ]: 9096 : },
289 : 36108 : [&] {
290 : : // base64 encoded psbt
291 : 36108 : std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
292 [ + + ]: 36108 : if (!opt_psbt) {
293 : 817 : good_data = false;
294 : 817 : return;
295 : : }
296 : 35291 : DataStream data_stream{};
297 [ + - ]: 35291 : data_stream << *opt_psbt;
298 [ + - ]: 35291 : r = EncodeBase64(data_stream);
299 : 36108 : },
300 : 7585 : [&] {
301 : : // base58 encoded key
302 : 7585 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
303 [ + + ]: 7585 : if (!key.IsValid()) {
304 : 8 : good_data = false;
305 : 8 : return;
306 : : }
307 [ + - ]: 7577 : r = EncodeSecret(key);
308 : 7585 : },
309 : 7462 : [&] {
310 : : // hex encoded pubkey
311 : 7462 : CKey key = ConsumePrivateKey(fuzzed_data_provider);
312 [ + + ]: 7462 : if (!key.IsValid()) {
313 : 7 : good_data = false;
314 : 7 : return;
315 : : }
316 [ + - + - ]: 7455 : r = HexStr(key.GetPubKey());
317 : 7462 : });
318 : 157507 : return r;
319 : 0 : }
320 : :
321 : 9419 : std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
322 : : {
323 : 9419 : std::vector<std::string> scalar_arguments;
324 [ + + + + : 295784 : LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
+ + ]
325 : : {
326 [ + - ]: 277644 : scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
327 : : }
328 [ + - + - ]: 28257 : return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
329 : 9419 : }
330 : :
331 : 28104 : std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
332 : : {
333 [ + + ]: 28104 : return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
334 : : }
335 : :
336 : 1 : RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
337 : : {
338 [ + - + - ]: 2 : static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
339 : 1 : SetRPCWarmupFinished();
340 : 1 : return setup.get();
341 [ + - ]: 1 : }
342 : : }; // namespace
343 : :
344 : 1 : void initialize_rpc()
345 : : {
346 : 1 : rpc_testing_setup = InitializeRPCFuzzTestingSetup();
347 : 1 : const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
348 [ + + ]: 109 : for (const std::string& rpc_command : supported_rpc_commands) {
349 [ - + ]: 108 : 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();
350 : 108 : 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();
351 [ - + ]: 108 : if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
352 [ # # # # : 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";
# # # # #
# ]
353 : 0 : std::terminate();
354 : : }
355 [ - + ]: 108 : if (safe_for_fuzzing && not_safe_for_fuzzing) {
356 [ # # # # : 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";
# # # # #
# ]
357 : 0 : std::terminate();
358 : : }
359 : : }
360 : 1 : const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
361 [ - + ]: 1 : if (limit_to_rpc_command_env != nullptr) {
362 [ # # ]: 0 : g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
363 : : }
364 : 1 : }
365 : :
366 [ + - ]: 8349 : FUZZ_TARGET(rpc, .init = initialize_rpc)
367 : : {
368 : 7937 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
369 : 7937 : bool good_data{true};
370 : 7937 : SetMockTime(ConsumeTime(fuzzed_data_provider));
371 : 7937 : const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
372 [ - + - - ]: 7937 : if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
373 : : return;
374 : : }
375 [ + + ]: 7937 : 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();
376 [ + + ]: 7937 : if (!safe_for_fuzzing) {
377 : : return;
378 : : }
379 : 7928 : std::vector<std::string> arguments;
380 [ + + + + : 71048 : LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
+ + ]
381 : : {
382 [ + - ]: 56208 : arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
383 : : }
384 : 7928 : try {
385 [ + + ]: 7928 : rpc_testing_setup->CallRPC(rpc_command, arguments);
386 [ - + ]: 2930 : } catch (const UniValue& json_rpc_error) {
387 [ + - + - : 2930 : const std::string error_msg{json_rpc_error.find_value("message").get_str()};
+ - ]
388 [ + + ]: 2930 : if (error_msg.starts_with("Internal bug detected")) {
389 : : // Only allow the intentional internal bug
390 [ - + ]: 1 : assert(error_msg.find("trigger_internal_bug") != std::string::npos);
391 : : }
392 : 2930 : }
393 : 7937 : }
|