LCOV - code coverage report
Current view: top level - src/test/fuzz - fuzz.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 65.0 % 120 78
Test Date: 2025-01-22 04:09:46 Functions: 58.3 % 12 7
Branches: 41.5 % 130 54

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

Generated by: LCOV version 2.0-1