LCOV - code coverage report
Current view: top level - src/test/fuzz - fuzz.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 65.9 % 126 83
Test Date: 2026-03-31 04:13:53 Functions: 54.5 % 11 6
Branches: 37.0 % 192 71

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2009-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 <test/fuzz/fuzz.h>
       6                 :             : 
       7                 :             : #include <netaddress.h>
       8                 :             : #include <netbase.h>
       9                 :             : #include <test/fuzz/util/check_globals.h>
      10                 :             : #include <test/util/coverage.h>
      11                 :             : #include <test/util/random.h>
      12                 :             : #include <test/util/setup_common.h>
      13                 :             : #include <util/check.h>
      14                 :             : #include <util/fs.h>
      15                 :             : #include <util/sock.h>
      16                 :             : #include <util/time.h>
      17                 :             : 
      18                 :             : #include <algorithm>
      19                 :             : #include <csignal>
      20                 :             : #include <cstdint>
      21                 :             : #include <cstdio>
      22                 :             : #include <cstdlib>
      23                 :             : #include <cstring>
      24                 :             : #include <exception>
      25                 :             : #include <fstream>
      26                 :             : #include <functional>
      27                 :             : #include <iostream>
      28                 :             : #include <map>
      29                 :             : #include <memory>
      30                 :             : #include <random>
      31                 :             : #include <string>
      32                 :             : #include <tuple>
      33                 :             : #include <utility>
      34                 :             : #include <vector>
      35                 :             : 
      36                 :             : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && defined(__AFL_FUZZ_INIT)
      37                 :             : __AFL_FUZZ_INIT();
      38                 :             : #endif
      39                 :             : 
      40                 :             : /**
      41                 :             :  * A copy of the command line arguments that start with `--`.
      42                 :             :  * First `LLVMFuzzerInitialize()` is called, which saves the arguments to `g_args`.
      43                 :             :  * Later, depending on the fuzz test, `G_TEST_COMMAND_LINE_ARGUMENTS()` may be
      44                 :             :  * called by `BasicTestingSetup` constructor to fetch those arguments and store
      45                 :             :  * them in `BasicTestingSetup::m_node::args`.
      46                 :             :  */
      47                 :             : static std::vector<const char*> g_args;
      48                 :             : 
      49                 :           0 : static void SetArgs(int argc, char** argv) {
      50         [ #  # ]:           0 :     for (int i = 1; i < argc; ++i) {
      51                 :             :         // Only take into account arguments that start with `--`. The others are for the fuzz engine:
      52                 :             :         // `fuzz -runs=1 fuzz_corpora/address_deserialize --checkaddrman=5`
      53   [ #  #  #  #  :           0 :         if (strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == '-') {
                   #  # ]
      54                 :           0 :             g_args.push_back(argv[i]);
      55                 :             :         }
      56                 :             :     }
      57                 :           0 : }
      58                 :             : 
      59                 :         895 : const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
      60                 :         895 :     return g_args;
      61                 :             : };
      62                 :             : 
      63                 :       52441 : struct FuzzTarget {
      64                 :             :     const TypeTestOneInput test_one_input;
      65                 :             :     const FuzzTargetOptions opts;
      66                 :             : };
      67                 :             : 
      68                 :       52898 : auto& FuzzTargets()
      69                 :             : {
      70   [ +  +  +  - ]:       53126 :     static std::map<std::string_view, FuzzTarget> g_fuzz_targets;
      71                 :       52898 :     return g_fuzz_targets;
      72                 :             : }
      73                 :             : 
      74                 :       52441 : void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
      75                 :             : {
      76         [ -  + ]:       52441 :     const auto [it, ins]{FuzzTargets().try_emplace(name, std::move(target), std::move(opts))};
      77         [ -  + ]:       52441 :     Assert(ins);
      78                 :       52441 : }
      79                 :             : 
      80                 :             : static std::string_view g_fuzz_target;
      81                 :             : static const TypeTestOneInput* g_test_one_input{nullptr};
      82                 :             : 
      83                 :      122591 : static void test_one_input(FuzzBufferType buffer)
      84                 :             : {
      85                 :      122591 :     CheckGlobals check{};
      86   [ -  +  +  - ]:      122591 :     (*Assert(g_test_one_input))(buffer);
      87                 :      122591 : }
      88                 :             : 
      89                 :         895 : const std::function<std::string()> G_TEST_GET_FULL_NAME{[]{
      90                 :         895 :     return std::string{g_fuzz_target};
      91                 :             : }};
      92                 :             : 
      93                 :         229 : static void initialize()
      94                 :             : {
      95                 :         229 :     CheckGlobals check{};
      96                 :             :     // By default, make the RNG deterministic with a fixed seed. This will affect all
      97                 :             :     // randomness during the fuzz test, except:
      98                 :             :     // - GetStrongRandBytes(), which is used for the creation of private key material.
      99                 :             :     // - Randomness obtained before this call in g_rng_temp_path_init
     100         [ +  - ]:         229 :     SeedRandomStateForTest(SeedRand::ZEROS);
     101                 :             : 
     102                 :             :     // Set time to the genesis block timestamp for deterministic initialization.
     103         [ +  - ]:         229 :     SetMockTime(1231006505);
     104                 :             : 
     105                 :             :     // Terminate immediately if a fuzzing harness ever tries to create a socket.
     106                 :             :     // Individual tests can override this by pointing CreateSock to a mocked alternative.
     107                 :         229 :     CreateSock = [](int, int, int) -> std::unique_ptr<Sock> { std::terminate(); };
     108                 :             : 
     109                 :             :     // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
     110                 :       31457 :     g_dns_lookup = [](const std::string& name, bool allow_lookup) {
     111         [ -  + ]:       31228 :         if (allow_lookup) {
     112                 :           0 :             std::terminate();
     113                 :             :         }
     114                 :       31228 :         return WrappedGetAddrInfo(name, false);
     115                 :         229 :     };
     116                 :             : 
     117                 :         229 :     bool should_exit{false};
     118         [ +  + ]:         229 :     if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
     119   [ +  -  +  +  :         230 :         for (const auto& [name, t] : FuzzTargets()) {
                   +  + ]
     120         [ +  + ]:         229 :             if (t.opts.hidden) continue;
     121   [ +  -  +  - ]:         229 :             std::cout << name << std::endl;
     122                 :             :         }
     123                 :             :         should_exit = true;
     124                 :             :     }
     125         [ -  + ]:         229 :     if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
     126   [ #  #  #  #  :           0 :         std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
             #  #  #  # ]
     127         [ #  # ]:           0 :         std::ofstream out_stream{out_path, std::ios::binary};
     128   [ #  #  #  #  :           0 :         for (const auto& [name, t] : FuzzTargets()) {
                   #  # ]
     129         [ #  # ]:           0 :             if (t.opts.hidden) continue;
     130   [ #  #  #  # ]:           0 :             out_stream << name << std::endl;
     131                 :             :         }
     132                 :           0 :         should_exit = true;
     133                 :           0 :     }
     134         [ +  + ]:         229 :     if (should_exit) {
     135                 :           1 :         std::exit(EXIT_SUCCESS);
     136                 :             :     }
     137         [ +  - ]:         228 :     if (const auto* env_fuzz{std::getenv("FUZZ")}) {
     138                 :             :         // To allow for easier fuzz executable binary modification,
     139   [ +  -  +  -  :         456 :         static std::string g_copy{env_fuzz}; // create copy to avoid compiler optimizations, and
                   +  - ]
     140         [ +  - ]:         228 :         g_fuzz_target = g_copy.c_str();      // strip string after the first null-char.
     141                 :             :     } else {
     142   [ #  #  #  # ]:           0 :         std::cerr << "Must select fuzz target with the FUZZ env var." << std::endl;
     143   [ #  #  #  # ]:           0 :         std::cerr << "Hint: Set the PRINT_ALL_FUZZ_TARGETS_AND_ABORT=1 env var to see all compiled targets." << std::endl;
     144                 :           0 :         std::exit(EXIT_FAILURE);
     145                 :             :     }
     146                 :         228 :     const auto it = FuzzTargets().find(g_fuzz_target);
     147   [ +  -  -  + ]:         228 :     if (it == FuzzTargets().end()) {
     148   [ #  #  #  #  :           0 :         std::cerr << "No fuzz target compiled for " << g_fuzz_target << "." << std::endl;
             #  #  #  # ]
     149                 :           0 :         std::exit(EXIT_FAILURE);
     150                 :             :     }
     151                 :         228 :     if constexpr (!G_FUZZING_BUILD && !G_ABORT_ON_FAILED_ASSUME) {
     152                 :             :         std::cerr << "Must compile with -DBUILD_FOR_FUZZING=ON or in Debug mode to execute a fuzz target." << std::endl;
     153                 :             :         std::exit(EXIT_FAILURE);
     154                 :             :     }
     155                 :         228 :     if (!EnableFuzzDeterminism()) {
     156                 :             :         if (std::getenv("FUZZ_NONDETERMINISM")) {
     157                 :             :             std::cerr << "Warning: FUZZ_NONDETERMINISM env var set, results may be inconsistent with fuzz build" << std::endl;
     158                 :             :         } else {
     159                 :             :             g_enable_dynamic_fuzz_determinism = true;
     160                 :             :             assert(EnableFuzzDeterminism());
     161                 :             :         }
     162                 :             :     }
     163         [ -  + ]:         228 :     Assert(!g_test_one_input);
     164         [ +  - ]:         228 :     g_test_one_input = &it->second.test_one_input;
     165         [ +  - ]:         228 :     it->second.opts.init();
     166                 :             : 
     167         [ +  - ]:         228 :     ResetCoverageCounters();
     168                 :         228 : }
     169                 :             : 
     170                 :             : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
     171                 :           0 : static bool read_stdin(std::vector<uint8_t>& data)
     172                 :             : {
     173                 :           0 :     std::istream::char_type buffer[1024];
     174                 :           0 :     std::streamsize length;
     175         [ #  # ]:           0 :     while ((std::cin.read(buffer, 1024), length = std::cin.gcount()) > 0) {
     176                 :           0 :         data.insert(data.end(), buffer, buffer + length);
     177                 :             :     }
     178                 :           0 :     return length == 0;
     179                 :             : }
     180                 :             : #endif
     181                 :             : 
     182                 :             : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP)
     183                 :      122591 : static bool read_file(fs::path p, std::vector<uint8_t>& data)
     184                 :             : {
     185                 :      122591 :     uint8_t buffer[1024];
     186                 :      122591 :     FILE* f = fsbridge::fopen(p, "rb");
     187         [ +  - ]:      122591 :     if (f == nullptr) return false;
     188                 :     2489309 :     do {
     189                 :     2489309 :         const size_t length = fread(buffer, sizeof(uint8_t), sizeof(buffer), f);
     190         [ +  - ]:     2489309 :         if (ferror(f)) return false;
     191                 :     2489309 :         data.insert(data.end(), buffer, buffer + length);
     192         [ +  + ]:     2489309 :     } while (!feof(f));
     193                 :      122591 :     fclose(f);
     194                 :      122591 :     return true;
     195                 :             : }
     196                 :             : #endif
     197                 :             : 
     198                 :             : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP)
     199                 :             : static fs::path g_input_path;
     200                 :           0 : void signal_handler(int signal)
     201                 :             : {
     202         [ #  # ]:           0 :     if (signal == SIGABRT) {
     203                 :           0 :         std::cerr << "Error processing input " << g_input_path << std::endl;
     204                 :             :     } else {
     205                 :           0 :         std::cerr << "Unexpected signal " << signal << " received\n";
     206                 :             :     }
     207                 :           0 :     std::_Exit(EXIT_FAILURE);
     208                 :             : }
     209                 :             : #endif
     210                 :             : 
     211                 :             : // This function is used by libFuzzer
     212                 :           0 : extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
     213                 :             : {
     214                 :           0 :     test_one_input({data, size});
     215                 :           0 :     return 0;
     216                 :             : }
     217                 :             : 
     218                 :             : // This function is used by libFuzzer
     219                 :           0 : extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
     220                 :             : {
     221                 :           0 :     SetArgs(*argc, *argv);
     222                 :           0 :     initialize();
     223                 :           0 :     return 0;
     224                 :             : }
     225                 :             : 
     226                 :             : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
     227                 :         229 : int main(int argc, char** argv)
     228                 :             : {
     229                 :         229 :     initialize();
     230                 :             : #ifdef __AFL_LOOP
     231                 :             :     // Enable AFL persistent mode. Requires compilation using afl-clang-fast++.
     232                 :             :     // See fuzzing.md for details.
     233                 :             :     const uint8_t* buffer = __AFL_FUZZ_TESTCASE_BUF;
     234                 :             :     while (__AFL_LOOP(100000)) {
     235                 :             :         size_t buffer_len = __AFL_FUZZ_TESTCASE_LEN;
     236                 :             :         test_one_input({buffer, buffer_len});
     237                 :             :     }
     238                 :             : #else
     239                 :         228 :     std::vector<uint8_t> buffer;
     240         [ -  + ]:         228 :     if (argc <= 1) {
     241   [ #  #  #  # ]:           0 :         if (!read_stdin(buffer)) {
     242                 :             :             return 0;
     243                 :             :         }
     244   [ #  #  #  # ]:           0 :         test_one_input(buffer);
     245                 :             :         return 0;
     246                 :             :     }
     247                 :         228 :     std::signal(SIGABRT, signal_handler);
     248                 :         228 :     const auto start_time{Now<SteadySeconds>()};
     249                 :         228 :     int tested = 0;
     250         [ +  + ]:         456 :     for (int i = 1; i < argc; ++i) {
     251         [ +  - ]:         228 :         fs::path input_path(*(argv + i));
     252   [ +  -  +  - ]:         228 :         if (fs::is_directory(input_path)) {
     253                 :         228 :             std::vector<fs::path> files;
     254   [ +  -  +  -  :      123047 :             for (fs::directory_iterator it(input_path); it != fs::directory_iterator(); ++it) {
             +  +  +  + ]
     255   [ +  -  -  + ]:      122591 :                 if (!fs::is_regular_file(it->path())) continue;
     256         [ +  - ]:      122591 :                 files.emplace_back(it->path());
     257                 :           0 :             }
     258         [ +  - ]:         456 :             std::ranges::shuffle(files, std::mt19937{std::random_device{}()});
     259         [ +  + ]:      122819 :             for (const auto& input_path : files) {
     260         [ +  - ]:      122591 :                 g_input_path = input_path;
     261   [ +  -  +  -  :      122591 :                 Assert(read_file(input_path, buffer));
                   -  + ]
     262   [ -  +  +  - ]:      122591 :                 test_one_input(buffer);
     263                 :      122591 :                 ++tested;
     264         [ +  - ]:      245182 :                 buffer.clear();
     265                 :             :             }
     266                 :         228 :         } else {
     267         [ #  # ]:           0 :             g_input_path = input_path;
     268   [ #  #  #  #  :           0 :             Assert(read_file(input_path, buffer));
                   #  # ]
     269   [ #  #  #  # ]:           0 :             test_one_input(buffer);
     270                 :           0 :             ++tested;
     271         [ -  - ]:         228 :             buffer.clear();
     272                 :             :         }
     273                 :         228 :     }
     274                 :         228 :     const auto end_time{Now<SteadySeconds>()};
     275   [ +  -  +  -  :         228 :     std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << count_seconds(end_time - start_time) << "s." << std::endl;
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     276                 :             : #endif
     277                 :             :     return 0;
     278                 :         228 : }
     279                 :             : #endif
        

Generated by: LCOV version 2.0-1