Bitcoin Core  24.99.0
P2P Digital Currency
fuzz.cpp
Go to the documentation of this file.
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 <fs.h>
8 #include <netaddress.h>
9 #include <netbase.h>
10 #include <test/util/setup_common.h>
11 #include <util/check.h>
12 #include <util/sock.h>
13 #include <util/time.h>
14 
15 #include <csignal>
16 #include <cstdint>
17 #include <exception>
18 #include <fstream>
19 #include <functional>
20 #include <map>
21 #include <memory>
22 #include <string>
23 #include <tuple>
24 #include <unistd.h>
25 #include <vector>
26 
27 const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
28 
36 static std::vector<const char*> g_args;
37 
38 static void SetArgs(int argc, char** argv) {
39  for (int i = 1; i < argc; ++i) {
40  // Only take into account arguments that start with `--`. The others are for the fuzz engine:
41  // `fuzz -runs=1 fuzz_seed_corpus/address_deserialize_v2 --checkaddrman=5`
42  if (strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == '-') {
43  g_args.push_back(argv[i]);
44  }
45  }
46 }
47 
48 const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
49  return g_args;
50 };
51 
52 std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>>& FuzzTargets()
53 {
54  static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>> g_fuzz_targets;
55  return g_fuzz_targets;
56 }
57 
59 {
60  const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init), hidden);
61  Assert(it_ins.second);
62 }
63 
64 static std::string_view g_fuzz_target;
66 
67 void initialize()
68 {
69  // Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
70  CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
71 
72  // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
73  g_dns_lookup = [](const std::string& name, bool allow_lookup) {
74  if (allow_lookup) {
75  std::terminate();
76  }
77  return WrappedGetAddrInfo(name, false);
78  };
79 
80  bool should_abort{false};
81  if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
82  for (const auto& t : FuzzTargets()) {
83  if (std::get<2>(t.second)) continue;
84  std::cout << t.first << std::endl;
85  }
86  should_abort = true;
87  }
88  if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
89  std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
90  std::ofstream out_stream{out_path, std::ios::binary};
91  for (const auto& t : FuzzTargets()) {
92  if (std::get<2>(t.second)) continue;
93  out_stream << t.first << std::endl;
94  }
95  should_abort = true;
96  }
97  Assert(!should_abort);
98  g_fuzz_target = Assert(std::getenv("FUZZ"));
99  const auto it = FuzzTargets().find(g_fuzz_target);
100  if (it == FuzzTargets().end()) {
101  std::cerr << "No fuzzer for " << g_fuzz_target << "." << std::endl;
102  std::exit(EXIT_FAILURE);
103  }
105  g_test_one_input = &std::get<0>(it->second);
106  std::get<1>(it->second)();
107 }
108 
109 #if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
110 static bool read_stdin(std::vector<uint8_t>& data)
111 {
112  uint8_t buffer[1024];
113  ssize_t length = 0;
114  while ((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
115  data.insert(data.end(), buffer, buffer + length);
116  }
117  return length == 0;
118 }
119 #endif
120 
121 #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP)
122 static bool read_file(fs::path p, std::vector<uint8_t>& data)
123 {
124  uint8_t buffer[1024];
125  FILE* f = fsbridge::fopen(p, "rb");
126  if (f == nullptr) return false;
127  do {
128  const size_t length = fread(buffer, sizeof(uint8_t), sizeof(buffer), f);
129  if (ferror(f)) return false;
130  data.insert(data.end(), buffer, buffer + length);
131  } while (!feof(f));
132  fclose(f);
133  return true;
134 }
135 #endif
136 
137 #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) && !defined(__AFL_LOOP)
138 static fs::path g_input_path;
139 void signal_handler(int signal)
140 {
141  if (signal == SIGABRT) {
142  std::cerr << "Error processing input " << g_input_path << std::endl;
143  } else {
144  std::cerr << "Unexpected signal " << signal << " received\n";
145  }
146  std::_Exit(EXIT_FAILURE);
147 }
148 #endif
149 
150 // This function is used by libFuzzer
151 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
152 {
153  static const auto& test_one_input = *Assert(g_test_one_input);
154  test_one_input({data, size});
155  return 0;
156 }
157 
158 // This function is used by libFuzzer
159 extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
160 {
161  SetArgs(*argc, *argv);
162  initialize();
163  return 0;
164 }
165 
166 #if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
167 int main(int argc, char** argv)
168 {
169  initialize();
170  static const auto& test_one_input = *Assert(g_test_one_input);
171 #ifdef __AFL_INIT
172  // Enable AFL deferred forkserver mode. Requires compilation using
173  // afl-clang-fast++. See fuzzing.md for details.
174  __AFL_INIT();
175 #endif
176 
177 #ifdef __AFL_LOOP
178  // Enable AFL persistent mode. Requires compilation using afl-clang-fast++.
179  // See fuzzing.md for details.
180  while (__AFL_LOOP(1000)) {
181  std::vector<uint8_t> buffer;
182  if (!read_stdin(buffer)) {
183  continue;
184  }
185  test_one_input(buffer);
186  }
187 #else
188  std::vector<uint8_t> buffer;
189  if (argc <= 1) {
190  if (!read_stdin(buffer)) {
191  return 0;
192  }
193  test_one_input(buffer);
194  return 0;
195  }
196  std::signal(SIGABRT, signal_handler);
197  const auto start_time{Now<SteadySeconds>()};
198  int tested = 0;
199  for (int i = 1; i < argc; ++i) {
200  fs::path input_path(*(argv + i));
201  if (fs::is_directory(input_path)) {
202  for (fs::directory_iterator it(input_path); it != fs::directory_iterator(); ++it) {
203  if (!fs::is_regular_file(it->path())) continue;
204  g_input_path = it->path();
205  Assert(read_file(it->path(), buffer));
206  test_one_input(buffer);
207  ++tested;
208  buffer.clear();
209  }
210  } else {
211  g_input_path = input_path;
212  Assert(read_file(input_path, buffer));
213  test_one_input(buffer);
214  ++tested;
215  buffer.clear();
216  }
217  }
218  const auto end_time{Now<SteadySeconds>()};
219  std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << count_seconds(end_time - start_time) << "s." << std::endl;
220 #endif
221  return 0;
222 }
223 #endif
int main(int argc, char **argv)
#define Assert(val)
Identity function.
Definition: check.h:73
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:520
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:31
path(std::filesystem::path path)
Definition: fs.h:36
void initialize()
Definition: fuzz.cpp:67
std::map< std::string_view, std::tuple< TypeTestOneInput, TypeInitialize, TypeHidden > > & FuzzTargets()
Definition: fuzz.cpp:52
const std::function< void(const std::string &)> G_TEST_LOG_FUN
This is connected to the logger.
Definition: fuzz.cpp:27
static void SetArgs(int argc, char **argv)
Definition: fuzz.cpp:38
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden)
Definition: fuzz.cpp:58
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
Definition: fuzz.cpp:151
int LLVMFuzzerInitialize(int *argc, char ***argv)
Definition: fuzz.cpp:159
static TypeTestOneInput * g_test_one_input
Definition: fuzz.cpp:65
static std::string_view g_fuzz_target
Definition: fuzz.cpp:64
static std::vector< const char * > g_args
A copy of the command line arguments that start with --.
Definition: fuzz.cpp:36
const std::function< std::vector< const char * >)> G_TEST_COMMAND_LINE_ARGUMENTS
Retrieve the command line arguments.
Definition: fuzz.cpp:48
bool TypeHidden
Definition: fuzz.h:25
std::function< void(FuzzBufferType)> TypeTestOneInput
Definition: fuzz.h:23
std::function< void()> TypeInitialize
Definition: fuzz.h:24
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
std::vector< CNetAddr > WrappedGetAddrInfo(const std::string &name, bool allow_lookup)
Wrapper for getaddrinfo(3).
Definition: netbase.cpp:43
std::function< std::unique_ptr< Sock >const CService &)> CreateSock
Socket factory.
Definition: netbase.cpp:534
DNSLookupFn g_dns_lookup
Definition: netbase.cpp:85
const char * name
Definition: rest.cpp:46
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:56