LCOV - code coverage report
Current view: top level - src/test/fuzz - fuzz.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 70 123 56.9 %
Date: 2024-01-03 14:57:27 Functions: 7 18 38.9 %
Branches: 56 144 38.9 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2009-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 <test/fuzz/fuzz.h>
       6                 :            : 
       7                 :            : #include <netaddress.h>
       8                 :            : #include <netbase.h>
       9                 :            : #include <test/util/setup_common.h>
      10                 :            : #include <util/check.h>
      11                 :            : #include <util/fs.h>
      12                 :            : #include <util/sock.h>
      13                 :            : #include <util/time.h>
      14                 :            : 
      15                 :            : #include <csignal>
      16                 :            : #include <cstdint>
      17                 :            : #include <cstdio>
      18                 :            : #include <cstdlib>
      19                 :            : #include <cstring>
      20                 :            : #include <exception>
      21                 :            : #include <fstream>
      22                 :            : #include <functional>
      23                 :            : #include <iostream>
      24                 :            : #include <map>
      25                 :            : #include <memory>
      26                 :            : #include <string>
      27                 :            : #include <tuple>
      28                 :            : #include <unistd.h>
      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                 :          2 : 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_seed_corpus/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                 :          0 :         }
      54                 :          0 :     }
      55                 :          0 : }
      56                 :            : 
      57                 :          2 : const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
      58                 :          0 :     return g_args;
      59                 :            : };
      60                 :            : 
      61                 :        346 : struct FuzzTarget {
      62                 :            :     const TypeTestOneInput test_one_input;
      63                 :            :     const FuzzTargetOptions opts;
      64                 :            : };
      65                 :            : 
      66                 :        349 : auto& FuzzTargets()
      67                 :            : {
      68 [ +  + ][ +  - ]:        349 :     static std::map<std::string_view, FuzzTarget> g_fuzz_targets;
      69                 :        349 :     return g_fuzz_targets;
      70                 :            : }
      71                 :            : 
      72                 :        346 : void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
      73                 :            : {
      74         [ +  - ]:        346 :     const auto it_ins{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped in C++20 */ {std::move(target), std::move(opts)})};
      75                 :        346 :     Assert(it_ins.second);
      76                 :        346 : }
      77                 :            : 
      78                 :            : static std::string_view g_fuzz_target;
      79                 :            : static const TypeTestOneInput* g_test_one_input{nullptr};
      80                 :            : 
      81                 :          2 : void initialize()
      82                 :            : {
      83                 :            :     // Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
      84                 :          2 :     CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
      85                 :            : 
      86                 :            :     // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
      87                 :          2 :     g_dns_lookup = [](const std::string& name, bool allow_lookup) {
      88         [ #  # ]:          0 :         if (allow_lookup) {
      89                 :          0 :             std::terminate();
      90                 :            :         }
      91                 :          0 :         return WrappedGetAddrInfo(name, false);
      92                 :            :     };
      93                 :            : 
      94                 :          2 :     bool should_exit{false};
      95         [ +  + ]:          2 :     if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
      96         [ +  + ]:        174 :         for (const auto& [name, t] : FuzzTargets()) {
      97         [ +  + ]:        173 :             if (t.opts.hidden) continue;
      98                 :        172 :             std::cout << name << std::endl;
      99                 :            :         }
     100                 :          1 :         should_exit = true;
     101                 :          1 :     }
     102         [ +  - ]:          2 :     if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
     103                 :          0 :         std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
     104                 :          0 :         std::ofstream out_stream{out_path, std::ios::binary};
     105         [ #  # ]:          0 :         for (const auto& [name, t] : FuzzTargets()) {
     106         [ #  # ]:          0 :             if (t.opts.hidden) continue;
     107 [ #  # ][ #  # ]:          0 :             out_stream << name << std::endl;
     108                 :            :         }
     109                 :          0 :         should_exit = true;
     110                 :          0 :     }
     111         [ +  + ]:          2 :     if (should_exit) {
     112                 :          1 :         std::exit(EXIT_SUCCESS);
     113                 :            :     }
     114         [ +  - ]:          1 :     if (const auto* env_fuzz{std::getenv("FUZZ")}) {
     115                 :            :         // To allow for easier fuzz executable binary modification,
     116 [ +  - ][ -  + ]:          1 :         static std::string g_copy{env_fuzz}; // create copy to avoid compiler optimizations, and
                 [ -  + ]
     117                 :          1 :         g_fuzz_target = g_copy.c_str();      // strip string after the first null-char.
     118                 :          1 :     } else {
     119                 :          0 :         std::cerr << "Must select fuzz target with the FUZZ env var." << std::endl;
     120                 :          0 :         std::cerr << "Hint: Set the PRINT_ALL_FUZZ_TARGETS_AND_ABORT=1 env var to see all compiled targets." << std::endl;
     121                 :          0 :         std::exit(EXIT_FAILURE);
     122                 :            :     }
     123                 :          1 :     const auto it = FuzzTargets().find(g_fuzz_target);
     124         [ +  - ]:          1 :     if (it == FuzzTargets().end()) {
     125                 :          0 :         std::cerr << "No fuzz target compiled for " << g_fuzz_target << "." << std::endl;
     126                 :          0 :         std::exit(EXIT_FAILURE);
     127                 :            :     }
     128                 :          1 :     Assert(!g_test_one_input);
     129                 :          1 :     g_test_one_input = &it->second.test_one_input;
     130                 :          1 :     it->second.opts.init();
     131                 :          1 : }
     132                 :            : 
     133                 :            : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
     134                 :          0 : static bool read_stdin(std::vector<uint8_t>& data)
     135                 :            : {
     136                 :            :     uint8_t buffer[1024];
     137                 :          0 :     ssize_t length = 0;
     138         [ #  # ]:          0 :     while ((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
     139                 :          0 :         data.insert(data.end(), buffer, buffer + length);
     140                 :            :     }
     141                 :          0 :     return length == 0;
     142                 :            : }
     143                 :            : #endif
     144                 :            : 
     145                 :            : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP)
     146                 :        829 : static bool read_file(fs::path p, std::vector<uint8_t>& data)
     147                 :            : {
     148                 :            :     uint8_t buffer[1024];
     149                 :        829 :     FILE* f = fsbridge::fopen(p, "rb");
     150         [ +  - ]:        829 :     if (f == nullptr) return false;
     151                 :        829 :     do {
     152                 :       1091 :         const size_t length = fread(buffer, sizeof(uint8_t), sizeof(buffer), f);
     153         [ +  - ]:       1091 :         if (ferror(f)) return false;
     154                 :       1091 :         data.insert(data.end(), buffer, buffer + length);
     155         [ +  + ]:       1091 :     } while (!feof(f));
     156                 :        829 :     fclose(f);
     157                 :        829 :     return true;
     158                 :        829 : }
     159                 :            : #endif
     160                 :            : 
     161                 :            : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP)
     162                 :          2 : static fs::path g_input_path;
     163                 :          0 : void signal_handler(int signal)
     164                 :            : {
     165         [ #  # ]:          0 :     if (signal == SIGABRT) {
     166                 :          0 :         std::cerr << "Error processing input " << g_input_path << std::endl;
     167                 :          0 :     } else {
     168                 :          0 :         std::cerr << "Unexpected signal " << signal << " received\n";
     169                 :            :     }
     170                 :          0 :     std::_Exit(EXIT_FAILURE);
     171                 :            : }
     172                 :            : #endif
     173                 :            : 
     174                 :            : // This function is used by libFuzzer
     175                 :          0 : extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
     176                 :            : {
     177 [ #  # ][ #  # ]:          0 :     static const auto& test_one_input = *Assert(g_test_one_input);
                 [ #  # ]
     178                 :          0 :     test_one_input({data, size});
     179                 :          0 :     return 0;
     180                 :          0 : }
     181                 :            : 
     182                 :            : // This function is used by libFuzzer
     183                 :          0 : extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
     184                 :            : {
     185                 :          0 :     SetArgs(*argc, *argv);
     186                 :          0 :     initialize();
     187                 :          0 :     return 0;
     188                 :            : }
     189                 :            : 
     190                 :            : #if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
     191                 :          1 : int main(int argc, char** argv)
     192                 :            : {
     193                 :          1 :     initialize();
     194 [ +  - ][ -  + ]:          1 :     static const auto& test_one_input = *Assert(g_test_one_input);
                 [ +  - ]
     195                 :            : #ifdef __AFL_LOOP
     196                 :            :     // Enable AFL persistent mode. Requires compilation using afl-clang-fast++.
     197                 :            :     // See fuzzing.md for details.
     198                 :            :     const uint8_t* buffer = __AFL_FUZZ_TESTCASE_BUF;
     199                 :            :     while (__AFL_LOOP(100000)) {
     200                 :            :         size_t buffer_len = __AFL_FUZZ_TESTCASE_LEN;
     201                 :            :         test_one_input({buffer, buffer_len});
     202                 :            :     }
     203                 :            : #else
     204                 :          1 :     std::vector<uint8_t> buffer;
     205         [ +  - ]:          1 :     if (argc <= 1) {
     206 [ #  # ][ #  # ]:          0 :         if (!read_stdin(buffer)) {
     207                 :          0 :             return 0;
     208                 :            :         }
     209 [ #  # ][ #  # ]:          0 :         test_one_input(buffer);
     210                 :          0 :         return 0;
     211                 :            :     }
     212                 :          1 :     std::signal(SIGABRT, signal_handler);
     213         [ +  - ]:          1 :     const auto start_time{Now<SteadySeconds>()};
     214                 :          1 :     int tested = 0;
     215         [ +  + ]:          2 :     for (int i = 1; i < argc; ++i) {
     216         [ +  - ]:          1 :         fs::path input_path(*(argv + i));
     217 [ +  - ][ +  - ]:          1 :         if (fs::is_directory(input_path)) {
     218 [ +  - ][ +  + ]:        830 :             for (fs::directory_iterator it(input_path); it != fs::directory_iterator(); ++it) {
                 [ +  - ]
     219 [ +  - ][ +  - ]:        829 :                 if (!fs::is_regular_file(it->path())) continue;
     220 [ +  - ][ +  - ]:        829 :                 g_input_path = it->path();
     221 [ +  - ][ -  + ]:        829 :                 Assert(read_file(it->path(), buffer));
         [ +  - ][ +  - ]
     222 [ +  - ][ +  - ]:        829 :                 test_one_input(buffer);
     223                 :        829 :                 ++tested;
     224                 :        829 :                 buffer.clear();
     225                 :        829 :             }
     226                 :          1 :         } else {
     227         [ #  # ]:          0 :             g_input_path = input_path;
     228 [ #  # ][ #  # ]:          0 :             Assert(read_file(input_path, buffer));
                 [ #  # ]
     229 [ #  # ][ #  # ]:          0 :             test_one_input(buffer);
     230                 :          0 :             ++tested;
     231                 :          0 :             buffer.clear();
     232                 :            :         }
     233                 :          1 :     }
     234         [ +  - ]:          1 :     const auto end_time{Now<SteadySeconds>()};
     235 [ +  - ][ +  - ]:          1 :     std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << count_seconds(end_time - start_time) << "s." << std::endl;
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     236                 :            : #endif
     237                 :          1 :     return 0;
     238                 :          1 : }
     239                 :            : #endif

Generated by: LCOV version 1.14