LCOV - code coverage report
Current view: top level - src/test/fuzz - rpc.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 114 272 41.9 %
Date: 2023-09-26 12:08:55 Functions: 9 39 23.1 %

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

Generated by: LCOV version 1.14