Coverage Report

Created: 2026-10-28 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl_fuzzer/proto_fuzzer/allocation_fault_injection.h
Line
Count
Source
1
/*
2
 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3
 *
4
 * SPDX-License-Identifier: curl
5
 */
6
7
#ifndef PROTO_FUZZER_ALLOCATION_FAULT_INJECTION_H_
8
#define PROTO_FUZZER_ALLOCATION_FAULT_INJECTION_H_
9
10
#include <cstddef>
11
#include <cstdint>
12
13
namespace proto_fuzzer::allocation_fault_injection {
14
15
namespace internal {
16
17
/// Nesting depth for harness-only allocations excluded from fault injection.
18
inline thread_local std::size_t failure_suppression_depth = 0;
19
/// Invalidates live guards when Reset() repairs state after a non-local exit.
20
inline thread_local std::uint64_t failure_suppression_generation = 0;
21
22
/// Enter one nestable suppression scope on the calling thread.
23
/// @return Generation captured by the matching scope guard.
24
225k
inline std::uint64_t BeginFailureSuppression() noexcept {
25
225k
  ++failure_suppression_depth;
26
225k
  return failure_suppression_generation;
27
225k
}
28
29
/// Leave a suppression scope unless Reset() already invalidated its guard.
30
/// @param generation Generation captured when the guard was constructed.
31
225k
inline void EndFailureSuppression(std::uint64_t generation) noexcept {
32
225k
  if (generation == failure_suppression_generation && failure_suppression_depth != 0) {
33
225k
    --failure_suppression_depth;
34
225k
  }
35
225k
}
36
37
/// @return Whether injected failures are suppressed on the calling thread.
38
5.55M
inline bool FailuresSuppressed() noexcept { return failure_suppression_depth != 0; }
39
40
/// Clear leaked suppression after a standalone-runner non-local exit.
41
486k
inline void ResetFailureSuppression() noexcept {
42
486k
  failure_suppression_depth = 0;
43
486k
  ++failure_suppression_generation;
44
486k
}
45
46
}  // namespace internal
47
48
/**
49
 * Exclude a narrow harness-only operation from the allocation-failure plan.
50
 *
51
 * Scopes nest on the calling thread. Positive-size libcurl allocations made
52
 * inside them bypass injected failures without advancing the active ordinal
53
 * sequence, so failures remain pending for subsequent injectable allocations.
54
 * Construct and destroy each guard on the same thread. Reset() invalidates any
55
 * live guards after a non-local exit and makes their later destruction a no-op.
56
 */
57
class ScopedFailureSuppression final {
58
 public:
59
225k
  ScopedFailureSuppression() noexcept : generation_(internal::BeginFailureSuppression()) {}
60
225k
  ~ScopedFailureSuppression() { internal::EndFailureSuppression(generation_); }
61
62
  ScopedFailureSuppression(const ScopedFailureSuppression&) = delete;
63
  ScopedFailureSuppression& operator=(const ScopedFailureSuppression&) = delete;
64
  ScopedFailureSuppression(ScopedFailureSuppression&&) = delete;
65
  ScopedFailureSuppression& operator=(ScopedFailureSuppression&&) = delete;
66
67
 private:
68
  /// Suppression generation used to make destruction safe across Reset().
69
  std::uint64_t generation_;
70
};
71
72
/** Snapshot of the calling thread's current allocation-failure plan. */
73
struct Statistics {
74
  /// Positive-size allocation calls observed since the last Arm().
75
  std::uint64_t allocation_attempts = 0;
76
  /// Selected calls rejected since the last Arm().
77
  std::uint64_t injected_failures = 0;
78
  /// Distinct ordinals retained in the bounded plan.
79
  std::size_t configured_failures = 0;
80
  /// Whether allocation calls on this thread are currently observed.
81
  bool armed = false;
82
};
83
84
bool InitializeCurl() noexcept;
85
bool ReinitializeCurl() noexcept;
86
bool CurlInitializationSucceeded() noexcept;
87
bool ClaimCurlInitializationReference() noexcept;
88
void Configure(const std::uint32_t* ordinals, std::size_t count) noexcept;
89
void Arm() noexcept;
90
void Disarm() noexcept;
91
void Reset() noexcept;
92
Statistics GetStatistics() noexcept;
93
94
// These are the process-wide callbacks passed to curl_global_init_mem. They
95
// remain visible so the focused unit test can verify the allocator contracts
96
// without depending on a particular libcurl call's private allocation trace.
97
namespace internal {
98
99
void* Malloc(std::size_t size) noexcept;
100
void Free(void* pointer) noexcept;
101
void* Realloc(void* pointer, std::size_t size) noexcept;
102
char* Strdup(const char* string) noexcept;
103
void* Calloc(std::size_t count, std::size_t size) noexcept;
104
105
}  // namespace internal
106
}  // namespace proto_fuzzer::allocation_fault_injection
107
108
#endif  // PROTO_FUZZER_ALLOCATION_FAULT_INJECTION_H_