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