/src/curl_fuzzer/proto_fuzzer/fuzzer_main.cc
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al. |
3 | | * |
4 | | * SPDX-License-Identifier: curl |
5 | | */ |
6 | | |
7 | | /// @file |
8 | | /// @brief Shared execution for profile-split protobuf targets. |
9 | | |
10 | | #include "proto_fuzzer/fuzzer_main.h" |
11 | | |
12 | | #include <curl/curl.h> |
13 | | |
14 | | #include <cstdio> |
15 | | |
16 | | #include "proto_fuzzer/allocation_fault_injection.h" |
17 | | #include "proto_fuzzer/curl_global_lifecycle.h" |
18 | | #include "proto_fuzzer/scenario_runner.h" |
19 | | #include "proto_fuzzer/target_policy.h" |
20 | | |
21 | | namespace { |
22 | | |
23 | | // libFuzzer reuses one process, so the static object initializes curl once and |
24 | | // releases its global resolver/TLS state only after the engine has stopped. |
25 | | proto_fuzzer::CurlGlobalLifecycle kGlobalBootstrap; |
26 | | |
27 | | } // namespace |
28 | | |
29 | | namespace proto_fuzzer { |
30 | | |
31 | | /// Mutate one serialized generated profile message. |
32 | | /// @param data Serialized profile message buffer to mutate in place. |
33 | | /// @param size Number of input bytes currently stored in `data`. |
34 | | /// @param max_size Capacity of `data` available for the mutated message. |
35 | | /// @param seed Deterministic mutation seed supplied by libFuzzer. |
36 | | /// @param traits Generated profile binding for this executable. |
37 | | /// @return Number of serialized bytes in the mutated message. |
38 | | std::size_t ProtoFuzzerCustomMutator(std::uint8_t* data, std::size_t size, std::size_t max_size, unsigned int seed, |
39 | 0 | const ProfileFuzzerTraits& traits) { |
40 | 0 | allocation_fault_injection::Reset(); |
41 | 0 | if (traits.custom_mutator == nullptr) { |
42 | 0 | return 0; |
43 | 0 | } |
44 | 0 | return traits.custom_mutator(data, size, max_size, seed); |
45 | 0 | } |
46 | | |
47 | | /// Cross two serialized generated profile messages of the same concrete type. |
48 | | /// @param data1 Serialized first parent message. |
49 | | /// @param size1 Number of bytes in `data1`. |
50 | | /// @param data2 Serialized second parent message. |
51 | | /// @param size2 Number of bytes in `data2`. |
52 | | /// @param out Destination buffer for the crossed message. |
53 | | /// @param max_out_size Capacity of `out`. |
54 | | /// @param seed Deterministic crossover seed supplied by libFuzzer. |
55 | | /// @param traits Generated profile binding for this executable. |
56 | | /// @return Number of serialized bytes written to `out`. |
57 | | std::size_t ProtoFuzzerCustomCrossOver(const std::uint8_t* data1, std::size_t size1, const std::uint8_t* data2, |
58 | | std::size_t size2, std::uint8_t* out, std::size_t max_out_size, |
59 | 0 | unsigned int seed, const ProfileFuzzerTraits& traits) { |
60 | 0 | allocation_fault_injection::Reset(); |
61 | 0 | if (traits.custom_crossover == nullptr) { |
62 | 0 | return 0; |
63 | 0 | } |
64 | 0 | return traits.custom_crossover(data1, size1, data2, size2, out, max_out_size, seed); |
65 | 0 | } |
66 | | |
67 | | /// Load and execute one generated-profile input under its fixed target lane. |
68 | | /// @param data Serialized generated-profile input bytes. |
69 | | /// @param size Number of bytes available at `data`. |
70 | | /// @param traits Generated profile binding selected by the entrypoint. |
71 | | /// @return Always zero, as required by the libFuzzer entrypoint contract. |
72 | 245k | int ProtoFuzzerTestOneInput(const std::uint8_t* data, std::size_t size, const ProfileFuzzerTraits& traits) { |
73 | 245k | allocation_fault_injection::Reset(); |
74 | 245k | #if defined(PROTO_FUZZER_OBSERVE_STATE) |
75 | 245k | if (traits.probe_mutation_helpers != nullptr) { |
76 | 10.2k | traits.probe_mutation_helpers(); |
77 | 10.2k | } |
78 | 245k | #endif |
79 | 245k | curl::fuzzer::proto::Scenario scenario; |
80 | 245k | if (traits.load_input == nullptr || !traits.load_input(data, size, &scenario)) { |
81 | 4.39k | return 0; |
82 | 4.39k | } |
83 | | |
84 | 240k | NormalizeScenarioForExecution(&scenario); |
85 | 240k | allocation_fault_injection::Configure(scenario.fail_allocations().data(), |
86 | 240k | static_cast<std::size_t>(scenario.fail_allocations_size())); |
87 | 240k | allocation_fault_injection::Arm(); |
88 | 240k | proto_fuzzer::RunScenario(scenario, RunModeFor(traits.profile)); |
89 | 240k | allocation_fault_injection::Reset(); |
90 | 240k | if (traits.profile == TargetProfile::kResolver && |
91 | 7.85k | scenario.resolver_mode() == curl::fuzzer::proto::RESOLVER_MODE_GLOBAL_LIFECYCLE) { |
92 | 143 | curl_global_cleanup(); |
93 | 143 | if (!allocation_fault_injection::ReinitializeCurl()) { |
94 | 0 | std::fputs("warning: libcurl reinitialization failed\n", stderr); |
95 | 0 | (void)curl_global_init(CURL_GLOBAL_ALL); |
96 | 0 | } |
97 | 143 | } |
98 | 240k | return 0; |
99 | 245k | } |
100 | | |
101 | | } // namespace proto_fuzzer |