curl-fuzzer C++ reference
Loading...
Searching...
No Matches
profile_fuzzer.h
Go to the documentation of this file.
1/*
2 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3 *
4 * SPDX-License-Identifier: curl
5 */
6
9
10#ifndef PROTO_FUZZER_PROFILE_FUZZER_H_
11#define PROTO_FUZZER_PROFILE_FUZZER_H_
12
13#include <libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h>
14
15#include <array>
16#include <cstddef>
17#include <cstdint>
18
19#include "curl_fuzzer.pb.h"
22
23namespace proto_fuzzer {
24
32 using CustomMutator = std::size_t (*)(std::uint8_t*, std::size_t, std::size_t, unsigned int);
34 using CustomCrossOver = std::size_t (*)(const std::uint8_t*, std::size_t, const std::uint8_t*, std::size_t,
35 std::uint8_t*, std::size_t, unsigned int);
37 using InputLoader = bool (*)(const std::uint8_t*, std::size_t, curl::fuzzer::proto::Scenario*);
39 using MutationProbe = void (*)();
40
51};
52
68template <typename Traits>
70 public:
72 using Message = typename Traits::Message;
74 using RuntimeScenario = curl::fuzzer::proto::Scenario;
75
77 static void EnsurePostProcessor() {
78 static const protobuf_mutator::libfuzzer::PostProcessorRegistration<Message> registration(
79 [](Message* message, unsigned int /*seed*/) { NormalizeMessage(message); });
80 (void)registration;
81 }
82
86 static void NormalizeMessage(Message* message) {
87 if (message == nullptr) {
88 return;
89 }
90
91 message->DiscardUnknownFields();
92 RuntimeScenario runtime;
93 Traits::Lower(message, &runtime);
94 if constexpr (Traits::kProfile != TargetProfile::kCompatibility) {
95 NormalizeScenarioForTarget(&runtime, Traits::kProfile, &Traits::PruneConditionalEligibility);
96 }
97 message->Clear();
98 Traits::Lift(&runtime, message);
99 }
100
107 static std::size_t CustomMutator(std::uint8_t* data, std::size_t size, std::size_t max_size, unsigned int seed) {
109 Message message;
110 return protobuf_mutator::libfuzzer::CustomProtoMutator(true, data, size, max_size, seed, &message);
111 }
112
122 static std::size_t CustomCrossOver(const std::uint8_t* data1, std::size_t size1, const std::uint8_t* data2,
123 std::size_t size2, std::uint8_t* out, std::size_t max_out_size,
124 unsigned int seed) {
126 Message message1;
127 Message message2;
128 return protobuf_mutator::libfuzzer::CustomProtoCrossOver(true, data1, size1, data2, size2, out, max_out_size, seed,
129 &message1, &message2);
130 }
131
141 static bool LoadAndLower(const std::uint8_t* data, std::size_t size, RuntimeScenario* runtime) {
142 if (runtime == nullptr) {
143 return false;
144 }
145
147 Message message;
148 if (!protobuf_mutator::libfuzzer::LoadProtoInput(true, data, size, &message)) {
149 return false;
150 }
151
152 // Mutated inputs have already passed the postprocessor before entering
153 // LPM's cache. Parsed inputs pass it in Fix(). Discarding once more is
154 // harmless and makes the lowering boundary fail closed for either path.
155 message.DiscardUnknownFields();
156 runtime->Clear();
157 Traits::Lower(&message, runtime);
158 // A projected profile root intentionally cannot carry policy-owned state
159 // such as a fixed scheme. The postprocessor normalized that state before
160 // Lift, but Lift necessarily omitted it; restore it at the final lowering
161 // boundary instead of assuming the serialized root can round-trip it.
162 if constexpr (Traits::kProfile != TargetProfile::kCompatibility) {
163 NormalizeScenarioForTarget(runtime, Traits::kProfile, &Traits::PruneConditionalEligibility);
164 }
165 return true;
166 }
167};
168
175template <typename Traits>
176bool LoadProfileInput(const std::uint8_t* data, std::size_t size, curl::fuzzer::proto::Scenario* runtime) {
177 return ProfileFuzzerHooks<Traits>::LoadAndLower(data, size, runtime);
178}
179
182template <typename Traits>
184 static const bool mutation_helpers_probed = [] {
185 constexpr std::size_t kBufferSize = 1024;
186 constexpr unsigned int kMutationSeed = 0x4355524cU;
187
188 curl::fuzzer::proto::Scenario runtime;
189 NormalizeScenarioForTarget(&runtime, Traits::kProfile, &Traits::PruneConditionalEligibility);
190
191 typename Traits::Message message;
192 Traits::Lift(&runtime, &message);
193 std::array<std::uint8_t, kBufferSize> first = {};
194 const std::size_t original_size = message.ByteSizeLong();
195 if (original_size > first.size()) {
196 return true;
197 }
198 (void)message.SerializeWithCachedSizesToArray(first.data());
199
200 using Hooks = ProfileFuzzerHooks<Traits>;
201 std::array<std::uint8_t, kBufferSize> second = first;
202 std::array<std::uint8_t, kBufferSize> child = {};
203 const std::size_t mutated_size = Hooks::CustomMutator(first.data(), original_size, first.size(), kMutationSeed);
204 const std::size_t crossed_size = Hooks::CustomCrossOver(first.data(), mutated_size, second.data(), original_size,
205 child.data(), child.size(), kMutationSeed);
206 const volatile std::size_t result_sizes[] = {mutated_size, crossed_size};
207 (void)result_sizes;
208 return true;
209 }();
210 (void)mutation_helpers_probed;
211}
212
216template <typename Traits>
218 using Hooks = ProfileFuzzerHooks<Traits>;
219 if constexpr (Traits::kProfile == TargetProfile::kTiming) {
220 return {Traits::kProfile, &Hooks::CustomMutator, &Hooks::CustomCrossOver, &LoadProfileInput<Traits>,
222 }
223 return {Traits::kProfile, &Hooks::CustomMutator, &Hooks::CustomCrossOver, &LoadProfileInput<Traits>, nullptr};
224}
225
226} // namespace proto_fuzzer
227
228#endif // PROTO_FUZZER_PROFILE_FUZZER_H_
Bind all LPM operations for one process to one protobuf message type.
static std::size_t CustomCrossOver(const std::uint8_t *data1, std::size_t size1, const std::uint8_t *data2, std::size_t size2, std::uint8_t *out, std::size_t max_out_size, unsigned int seed)
Cross two binary profile messages using the same type as mutation/load.
static void NormalizeMessage(Message *message)
Remove unknown wire data, normalize through the common runtime model, and lift back into the same gen...
static void EnsurePostProcessor()
Register the profile postprocessor before the first LPM operation.
static std::size_t CustomMutator(std::uint8_t *data, std::size_t size, std::size_t max_size, unsigned int seed)
Mutate a binary profile message and retain its exact type in LPM's cache.
curl::fuzzer::proto::Scenario RuntimeScenario
Shared internal Scenario consumed by the runtime.
static bool LoadAndLower(const std::uint8_t *data, std::size_t size, RuntimeScenario *runtime)
Load one binary profile message and transfer it into owned runtime state.
typename Traits::Message Message
Concrete generated protobuf root for this target profile.
void ProbeTimingMutationHelpers()
Exercise the typed mutation ABI once for the profiling timing target.
bool LoadProfileInput(const std::uint8_t *data, std::size_t size, curl::fuzzer::proto::Scenario *runtime)
Load one generated profile message and lower it into runtime state.
constexpr ProfileFuzzerTraits BindProfileFuzzer()
Bind one generated adapter to the ordinary entrypoint helper interface.
Ordinary runtime binding for one generated protobuf profile.
bool(*)(const std::uint8_t *, std::size_t, curl::fuzzer::proto::Scenario *) InputLoader
Parse and lower one input using the bound message type.
CustomCrossOver custom_crossover
Typed crossover callback.
CustomMutator custom_mutator
Typed mutation callback.
std::size_t(*)(const std::uint8_t *, std::size_t, const std::uint8_t *, std::size_t, std::uint8_t *, std::size_t, unsigned int) CustomCrossOver
libFuzzer custom-crossover callback for the bound message type.
void(*)() MutationProbe
Optional profiling-only callback that exercises typed mutation helpers.
TargetProfile profile
Runtime policy permanently associated with all callbacks.
MutationProbe probe_mutation_helpers
Timing-profile probe, or nullptr for ordinary profiles.
std::size_t(*)(std::uint8_t *, std::size_t, std::size_t, unsigned int) CustomMutator
libFuzzer custom-mutator callback for the bound message type.
InputLoader load_input
Typed parse-and-lower callback.
void NormalizeScenarioForTarget(curl::fuzzer::proto::Scenario *scenario, TargetProfile profile, ConditionalEligibilityPruner prune_conditional_eligibility)
Complete normalization for a loaded or mutated fixed-target Scenario.
Mutation policies that keep each proto fuzzer in one intentional performance and protocol lane.
Compile-time identities and execution modes for proto fuzz targets.
TargetProfile
Identifies one compiled proto-fuzzer lane.
@ kTiming
Isolate deterministic backpressure and timed-wait behavior.
@ kCompatibility
Preserve the original mixed target and its accumulated corpus exactly.