-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathfaultInjection.h
More file actions
139 lines (116 loc) · 5.62 KB
/
Copy pathfaultInjection.h
File metadata and controls
139 lines (116 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Compile-time fault-injection layer for the profiler's memory-access sites.
//
// The macros below wrap a pointer or value expression at a real dereference
// site (VMStructs::at, walkVM, walkFP, walkDwarf). When __FAULT_INJECTION__ is
// defined, each wrapped expression, with the tier's probability, is replaced by
// a deliberately bad address (so the load faults and the profiler's recovery
// path — SafeAccess safefetch or walkVM's sigsetjmp/siglongjmp — is exercised) or a
// random int/long value. When the flag is NOT defined, every macro is a strict
// identity: it expands to exactly the parenthesized original expression, with
// unchanged type and value category and zero runtime cost.
//
// pc = SafeAccess::load(INJECT_FAULT_ADDRESS_LIKELY((void**)fp));
// VMMethod* m = ((VMMethod**)INJECT_FAULT_ADDRESS_UNLIKELY(fp))[off];
//
// INJECT_FAULT_BOOL_* wraps the *result* of a call that already ran for
// real, forcing it to report `false` so a caller's failure-handling path
// (not its memory-safety recovery path) gets exercised, e.g.:
//
// return INJECT_FAULT_BOOL_LIKELY(dlopen(name, flags) != nullptr);
//
// The three tiers name their firing frequency: RARE 0.01%, UNLIKELY 0.1%,
// LIKELY 1%. See faultInjection.cpp for the poison-address and PRNG details.
#ifndef _FAULT_INJECTION_H
#define _FAULT_INJECTION_H
#include <cassert>
#ifdef __FAULT_INJECTION__
#include "arch.h" // u64
#include <cstdint>
#define NO_INJECTION_ASSERT(a)
namespace faultinj {
// Firing probability expressed as an xorshift64 threshold (round(p * 2^64)), so
// the hot-path check is a single integer compare (rng < threshold) with no
// floating point in the signal handler.
constexpr u64 PROB_RARE = 1844674407370955ULL; // 1e-4 (0.01%)
constexpr u64 PROB_UNLIKELY = 18446744073709552ULL; // 1e-3 (0.1%)
constexpr u64 PROB_LIKELY = 184467440737095520ULL; // 1e-2 (1%)
// Called once at profiler startup (off the signal path) to mmap the PROT_NONE
// guard region used by poisonAddress(). Safe to call before any injection.
void init();
// Draws one per-thread (or global-fallback) PRNG value. Async-signal-safe.
u64 nextRandom();
// Returns true with probability threshold/2^64. The function name perturbs the
// draw so distinct call sites get statistically independent decisions.
bool shouldFire(u64 threshold, const char* fn);
// A word-aligned address intended to fault on access. When init() has reserved the
// mmap'd PROT_NONE guard region, this returns an address inside it (deterministic
// SIGSEGV). If init() failed, it falls back to a best-effort garbage address.
uintptr_t poisonAddress();
// Returns ptr unchanged, or a poison address (cast to T) when the tier fires.
// Templated so the wrapped expression's static type (void**, const char*,
// uintptr_t, ...) is preserved exactly.
template <typename T>
inline T injectAddress(T ptr, u64 threshold, const char* fn) {
if (__builtin_expect(shouldFire(threshold, fn), 0)) {
// C-style cast intentionally: converts the numeric poison address to any
// pointer type or to uintptr_t. This code only ever compiles under the flag.
return (T)poisonAddress();
}
return ptr;
}
// Returns orig unchanged, or `faulty` when the tier fires. Unlike
// injectAddress() (which fakes an input about to be dereferenced), this fakes
// the *outcome* of a call that already ran for real — e.g. making a
// successful dlopen() appear to have failed, to exercise a caller's error
// path without needing the library to actually be absent.
template <typename T>
inline T injectValue(T orig, T faulty, u64 threshold, const char* fn) {
if (__builtin_expect(shouldFire(threshold, fn), 0)) {
return faulty;
}
return orig;
}
} // namespace faultinj
#define INJECT_FAULT_ADDRESS_RARE(ptr) \
::faultinj::injectAddress((ptr), ::faultinj::PROB_RARE, __func__)
#define INJECT_FAULT_ADDRESS_UNLIKELY(ptr) \
::faultinj::injectAddress((ptr), ::faultinj::PROB_UNLIKELY, __func__)
#define INJECT_FAULT_ADDRESS_LIKELY(ptr) \
::faultinj::injectAddress((ptr), ::faultinj::PROB_LIKELY, __func__)
#define INJECT_FAULT_BOOL_RARE(v) \
::faultinj::injectValue((v), false, ::faultinj::PROB_RARE, __func__)
#define INJECT_FAULT_BOOL_UNLIKELY(v) \
::faultinj::injectValue((v), false, ::faultinj::PROB_UNLIKELY, __func__)
#define INJECT_FAULT_BOOL_LIKELY(v) \
::faultinj::injectValue((v), false, ::faultinj::PROB_LIKELY, __func__)
#else // __FAULT_INJECTION__ not defined — strict identity, zero cost.
#define INJECT_FAULT_ADDRESS_RARE(ptr) (ptr)
#define INJECT_FAULT_ADDRESS_UNLIKELY(ptr) (ptr)
#define INJECT_FAULT_ADDRESS_LIKELY(ptr) (ptr)
#define INJECT_FAULT_INT_RARE(v) (v)
#define INJECT_FAULT_INT_UNLIKELY(v) (v)
#define INJECT_FAULT_INT_LIKELY(v) (v)
#define INJECT_FAULT_LONG_RARE(v) (v)
#define INJECT_FAULT_LONG_UNLIKELY(v) (v)
#define INJECT_FAULT_LONG_LIKELY(v) (v)
#define INJECT_FAULT_BOOL_RARE(v) (v)
#define INJECT_FAULT_BOOL_UNLIKELY(v) (v)
#define INJECT_FAULT_BOOL_LIKELY(v) (v)
#define NO_INJECTION_ASSERT(a) (assert(a))
#endif // __FAULT_INJECTION__
#endif // _FAULT_INJECTION_H