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