LCOV - code coverage report
Current view: top level - src/test/fuzz - fuzz.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 63.2 % 117 74
Test Date: 2024-12-04 04:00:22 Functions: 54.5 % 11 6
Branches: 39.9 % 138 55

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

Generated by: LCOV version 2.0-1