Coverage Report

Created: 2025-09-25 19:17

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