7#include "proto_fuzzer/allocation_fault_injection.h"
21namespace proto_fuzzer::allocation_fault_injection {
28 std::array<std::uint32_t, scenario_limits::kMaxAllocationFailures> ordinals{};
30 std::size_t ordinal_count = 0;
32 std::size_t next_ordinal = 0;
34 std::uint64_t allocation_attempts = 0;
36 std::uint64_t injected_failures = 0;
42thread_local ThreadState state;
44std::once_flag initialize_once;
46bool callbacks_active =
false;
48bool curl_initialized =
false;
50std::atomic<bool> curl_initialization_claimed{
false};
56std::uint32_t CanonicalOrdinal(std::uint32_t ordinal)
noexcept {
60 const std::uint32_t maximum =
static_cast<std::uint32_t
>(scenario_limits::kMaxAllocationOrdinal);
61 return 1U + ((ordinal - 1U) % maximum);
68void InsertOrdinal(std::uint32_t ordinal)
noexcept {
72 const auto begin = state.ordinals.begin();
73 const auto end = begin +
static_cast<std::ptrdiff_t
>(state.ordinal_count);
74 const auto position = std::lower_bound(begin, end, ordinal);
75 if (position != end && *position == ordinal) {
78 const std::size_t index =
static_cast<std::size_t
>(position - begin);
79 if (index >= state.ordinals.size()) {
82 if (state.ordinal_count < state.ordinals.size()) {
83 ++state.ordinal_count;
85 for (std::size_t destination = state.ordinal_count - 1; destination > index; --destination) {
86 state.ordinals[destination] = state.ordinals[destination - 1];
88 state.ordinals[index] = ordinal;
97bool ShouldFail(
bool positive_size)
noexcept {
98 if (!positive_size || !state.armed || internal::FailuresSuppressed()) {
101 ++state.allocation_attempts;
102 if (state.next_ordinal < state.ordinal_count && state.allocation_attempts == state.ordinals[state.next_ordinal]) {
103 ++state.next_ordinal;
104 ++state.injected_failures;
119bool InitializeCurl() noexcept {
120 std::call_once(initialize_once, [] {
121 if (curl_global_init_mem(CURL_GLOBAL_ALL, internal::Malloc, internal::Free, internal::Realloc, internal::Strdup,
122 internal::Calloc) != CURLE_OK) {
125 curl_initialized =
true;
128 char* probe = curl_escape(
"allocator callback probe", 0);
129 callbacks_active = state.allocation_attempts != 0;
132 state = ThreadState{};
134 return callbacks_active;
140bool ReinitializeCurl() noexcept {
141 return curl_global_init_mem(CURL_GLOBAL_ALL, internal::Malloc, internal::Free, internal::Realloc, internal::Strdup,
142 internal::Calloc) == CURLE_OK;
148bool CurlInitializationSucceeded() noexcept {
149 (void)InitializeCurl();
150 return curl_initialized;
157bool ClaimCurlInitializationReference() noexcept {
158 (void)InitializeCurl();
159 if (!curl_initialized) {
162 bool expected =
false;
163 return curl_initialization_claimed.compare_exchange_strong(expected,
true, std::memory_order_relaxed,
164 std::memory_order_relaxed);
173void Configure(
const std::uint32_t* ordinals, std::size_t count)
noexcept {
174 state = ThreadState{};
175 if (ordinals ==
nullptr) {
178 for (std::size_t index = 0; index < count; ++index) {
179 InsertOrdinal(CanonicalOrdinal(ordinals[index]));
187 state.next_ordinal = 0;
188 state.allocation_attempts = 0;
189 state.injected_failures = 0;
190 state.armed = state.ordinal_count != 0;
196void ArmNextAllocationFailure() noexcept {
197 state = ThreadState{};
198 state.ordinals[0] = 1;
199 state.ordinal_count = 1;
204void Disarm() noexcept { state.armed =
false; }
209void Reset() noexcept {
210 state = ThreadState{};
211 internal::ResetFailureSuppression();
217 return {state.allocation_attempts, state.injected_failures, state.ordinal_count,
218 state.armed && !internal::FailuresSuppressed()};
224void* Malloc(std::size_t size)
noexcept {
return ShouldFail(size != 0) ? nullptr : std::malloc(size); }
227void Free(
void* pointer)
noexcept { std::free(pointer); }
231void* Realloc(
void* pointer, std::size_t size)
noexcept {
232 return ShouldFail(size != 0) ? nullptr : std::realloc(pointer, size);
236char* Strdup(
const char*
string)
noexcept {
237 if (
string ==
nullptr) {
240 const std::size_t length = std::strlen(
string) + 1;
241 if (ShouldFail(length != 0)) {
244 char* copy =
static_cast<char*
>(std::malloc(length));
245 if (copy !=
nullptr) {
246 std::memcpy(copy,
string, length);
253void* Calloc(std::size_t count, std::size_t size)
noexcept {
254 return ShouldFail(count != 0 && size != 0) ? nullptr : std::calloc(count, size);
Shared complexity limits for structured scenarios.
Snapshot of the calling thread's current allocation-failure plan.