LCOV - code coverage report
Current view: top level - src/test/fuzz - fuzz.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 72 123 58.5 %
Date: 2023-09-26 12:08:55 Functions: 13 20 65.0 %

          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           2 : 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           3 : const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
      58           1 :     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         348 :     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        4859 : static bool read_file(fs::path p, std::vector<uint8_t>& data)
     147             : {
     148             :     uint8_t buffer[1024];
     149        4859 :     FILE* f = fsbridge::fopen(p, "rb");
     150        4859 :     if (f == nullptr) return false;
     151        4859 :     do {
     152        4859 :         const size_t length = fread(buffer, sizeof(uint8_t), sizeof(buffer), f);
     153        4859 :         if (ferror(f)) return false;
     154        4859 :         data.insert(data.end(), buffer, buffer + length);
     155        4859 :     } while (!feof(f));
     156        4859 :     fclose(f);
     157        4859 :     return true;
     158        4859 : }
     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        4860 :             for (fs::directory_iterator it(input_path); it != fs::directory_iterator(); ++it) {
     219        4859 :                 if (!fs::is_regular_file(it->path())) continue;
     220        4859 :                 g_input_path = it->path();
     221        4859 :                 Assert(read_file(it->path(), buffer));
     222        4859 :                 test_one_input(buffer);
     223        4859 :                 ++tested;
     224        4859 :                 buffer.clear();
     225        4859 :             }
     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