/work/testing/fuzzing/fuzz_support.hh
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2021-2025 The TokTok team. |
3 | | */ |
4 | | |
5 | | #ifndef C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H |
6 | | #define C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H |
7 | | |
8 | | #include <array> |
9 | | #include <cassert> |
10 | | #include <cstdint> |
11 | | #include <cstdio> |
12 | | #include <cstdlib> |
13 | | #include <cstring> |
14 | | #include <deque> |
15 | | #include <memory> |
16 | | #include <utility> |
17 | | #include <vector> |
18 | | |
19 | | #include "../../toxcore/tox_private.h" |
20 | | |
21 | | struct Fuzz_Data { |
22 | | static constexpr bool FUZZ_DEBUG = false; |
23 | | static constexpr std::size_t TRACE_TRAP = -1; // 579; |
24 | | |
25 | | private: |
26 | | const uint8_t *data_; |
27 | | const uint8_t *base_; |
28 | | std::size_t size_; |
29 | | |
30 | | public: |
31 | | Fuzz_Data(const uint8_t *input_data, std::size_t input_size) |
32 | | : data_(input_data) |
33 | | , base_(input_data) |
34 | | , size_(input_size) |
35 | 2.57k | { |
36 | 2.57k | } |
37 | | |
38 | | Fuzz_Data &operator=(const Fuzz_Data &rhs) = delete; |
39 | | Fuzz_Data(const Fuzz_Data &rhs) = delete; |
40 | | |
41 | | struct Consumer { |
42 | | const char *func; |
43 | | Fuzz_Data &fd; |
44 | | |
45 | | operator bool() |
46 | 1.11k | { |
47 | | // Special case because memcpy causes UB for bool (which can't be |
48 | | // anything other than 0 or 1). |
49 | 1.11k | const bool val = fd.data_[0]; |
50 | 1.11k | if (FUZZ_DEBUG) { |
51 | 0 | std::printf("consume@%zu(%s): bool %s\n", fd.pos(), func, val ? "true" : "false"); |
52 | 0 | } |
53 | 1.11k | ++fd.data_; |
54 | 1.11k | --fd.size_; |
55 | 1.11k | return val; |
56 | 1.11k | } |
57 | | |
58 | | template <typename T> |
59 | | operator T() |
60 | 7.85k | { |
61 | 7.85k | const uint8_t *bytes = fd.consume(func, sizeof(T)); |
62 | 7.85k | T val; |
63 | 7.85k | std::memcpy(&val, bytes, sizeof(T)); |
64 | 7.85k | return val; |
65 | 7.85k | } _ZN9Fuzz_Data8ConsumercvT_IhEEv Line | Count | Source | 60 | 5.89k | { | 61 | 5.89k | const uint8_t *bytes = fd.consume(func, sizeof(T)); | 62 | 5.89k | T val; | 63 | 5.89k | std::memcpy(&val, bytes, sizeof(T)); | 64 | 5.89k | return val; | 65 | 5.89k | } |
_ZN9Fuzz_Data8ConsumercvT_ItEEv Line | Count | Source | 60 | 1.96k | { | 61 | 1.96k | const uint8_t *bytes = fd.consume(func, sizeof(T)); | 62 | 1.96k | T val; | 63 | 1.96k | std::memcpy(&val, bytes, sizeof(T)); | 64 | 1.96k | return val; | 65 | 1.96k | } |
|
66 | | }; |
67 | | |
68 | 8.97k | Consumer consume1(const char *func) { return Consumer{func, *this}; } |
69 | 21.0k | std::size_t size() const { return size_; } |
70 | 0 | std::size_t pos() const { return data_ - base_; } |
71 | 167 | const uint8_t *data() const { return data_; } |
72 | 4.88k | bool empty() const { return size_ == 0; } |
73 | | |
74 | | const uint8_t *consume(const char *func, std::size_t count) |
75 | 15.0k | { |
76 | 15.0k | const uint8_t *val = data_; |
77 | 15.0k | if (FUZZ_DEBUG) { |
78 | 0 | if (pos() == TRACE_TRAP) { |
79 | 0 | __asm__("int $3"); |
80 | 0 | } |
81 | 0 | if (count == 1) { |
82 | 0 | std::printf("consume@%zu(%s): %d (0x%02x)\n", pos(), func, val[0], val[0]); |
83 | 0 | } else if (count != 0) { |
84 | 0 | std::printf("consume@%zu(%s): %02x..%02x[%zu]\n", pos(), func, val[0], |
85 | 0 | val[count - 1], count); |
86 | 0 | } |
87 | 0 | } |
88 | 15.0k | data_ += count; |
89 | 15.0k | size_ -= count; |
90 | 15.0k | return val; |
91 | 15.0k | } |
92 | | }; |
93 | | |
94 | | /** @brief Consumes 1 byte of the fuzzer input or returns if no data available. |
95 | | * |
96 | | * This advances the fuzzer input data by 1 byte and consumes that byte in the |
97 | | * declaration. |
98 | | * |
99 | | * @example |
100 | | * @code |
101 | | * CONSUME1_OR_RETURN(const uint8_t, one_byte, input); |
102 | | * @endcode |
103 | | */ |
104 | | #define CONSUME1_OR_RETURN(TYPE, NAME, INPUT) \ |
105 | 7.90k | if (INPUT.size() < sizeof(TYPE)) { \ |
106 | 14 | return; \ |
107 | 14 | } \ |
108 | 7.90k | TYPE NAME = INPUT.consume1(__func__) |
109 | | |
110 | | /** @brief Consumes 1 byte of the fuzzer input or returns a value if no data |
111 | | * available. |
112 | | * |
113 | | * This advances the fuzzer input data by 1 byte and consumes that byte in the |
114 | | * declaration. |
115 | | * |
116 | | * @example |
117 | | * @code |
118 | | * CONSUME1_OR_RETURN_VAL(const uint8_t one_byte, input, nullptr); |
119 | | * @endcode |
120 | | */ |
121 | | #define CONSUME1_OR_RETURN_VAL(TYPE, NAME, INPUT, VAL) \ |
122 | 4.18k | if (INPUT.size() < sizeof(TYPE)) { \ |
123 | 3.10k | return VAL; \ |
124 | 3.10k | } \ |
125 | 4.18k | TYPE NAME = INPUT.consume1(__func__) |
126 | | |
127 | | /** @brief Consumes SIZE bytes of the fuzzer input or returns if not enough data available. |
128 | | * |
129 | | * This advances the fuzzer input data by SIZE byte and consumes those bytes in |
130 | | * the declaration. If less than SIZE bytes are available in the fuzzer input, |
131 | | * this macro returns from the enclosing function. |
132 | | * |
133 | | * @example |
134 | | * @code |
135 | | * CONSUME_OR_RETURN(const uint8_t *ten_bytes, input, 10); |
136 | | * @endcode |
137 | | */ |
138 | | #define CONSUME_OR_RETURN(DECL, INPUT, SIZE) \ |
139 | 7.12k | if (INPUT.size() < SIZE) { \ |
140 | 83 | return; \ |
141 | 83 | } \ |
142 | 7.12k | DECL = INPUT.consume(__func__, SIZE) |
143 | | |
144 | | #define CONSUME_OR_RETURN_VAL(DECL, INPUT, SIZE, VAL) \ |
145 | 189 | if (INPUT.size() < SIZE) { \ |
146 | 16 | return VAL; \ |
147 | 16 | } \ |
148 | 189 | DECL = INPUT.consume(__func__, SIZE) |
149 | | |
150 | | #define CONSUME_OR_ABORT(DECL, INPUT, SIZE) \ |
151 | 0 | if (INPUT.size() < SIZE) { \ |
152 | 0 | abort(); \ |
153 | 0 | } \ |
154 | 0 | DECL = INPUT.consume(__func__, SIZE) |
155 | | |
156 | | using Fuzz_Target = void (*)(Fuzz_Data &input); |
157 | | |
158 | | template <Fuzz_Target... Args> |
159 | | struct Fuzz_Target_Selector; |
160 | | |
161 | | template <Fuzz_Target Arg, Fuzz_Target... Args> |
162 | | struct Fuzz_Target_Selector<Arg, Args...> { |
163 | | static void select(uint8_t selector, Fuzz_Data &input) |
164 | 1.03k | { |
165 | 1.03k | if (selector == sizeof...(Args)) { |
166 | 478 | return Arg(input); |
167 | 478 | } |
168 | 558 | return Fuzz_Target_Selector<Args...>::select(selector, input); |
169 | 1.03k | } unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_117TestHandleRequestER9Fuzz_DataEEXadL_ZNS0_15TestUnpackNodesES2_EEEE6selectEhS2_ Line | Count | Source | 164 | 41 | { | 165 | 41 | if (selector == sizeof...(Args)) { | 166 | 6 | return Arg(input); | 167 | 6 | } | 168 | 35 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 41 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_115TestUnpackNodesER9Fuzz_DataEEEE6selectEhS2_ Line | Count | Source | 164 | 35 | { | 165 | 35 | if (selector == sizeof...(Args)) { | 166 | 34 | return Arg(input); | 167 | 34 | } | 168 | 1 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 35 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_122TestSendForwardRequestER9Fuzz_DataEEXadL_ZNS0_16TestForwardReplyES2_EEEE6selectEhS2_ Line | Count | Source | 164 | 53 | { | 165 | 53 | if (selector == sizeof...(Args)) { | 166 | 26 | return Arg(input); | 167 | 26 | } | 168 | 27 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 53 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_116TestForwardReplyER9Fuzz_DataEEEE6selectEhS2_ Line | Count | Source | 164 | 27 | { | 165 | 27 | if (selector == sizeof...(Args)) { | 166 | 26 | return Arg(input); | 167 | 26 | } | 168 | 1 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 27 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_123TestUnpackAnnouncesListER9Fuzz_DataEEXadL_ZNS0_24TestUnpackPublicAnnounceES2_EEXadL_ZNS0_9TestDoGcaES2_EEEE6selectEhS2_ Line | Count | Source | 164 | 339 | { | 165 | 339 | if (selector == sizeof...(Args)) { | 166 | 101 | return Arg(input); | 167 | 101 | } | 168 | 238 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 339 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_124TestUnpackPublicAnnounceER9Fuzz_DataEEXadL_ZNS0_9TestDoGcaES2_EEEE6selectEhS2_ Line | Count | Source | 164 | 238 | { | 165 | 238 | if (selector == sizeof...(Args)) { | 166 | 6 | return Arg(input); | 167 | 6 | } | 168 | 232 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 238 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_19TestDoGcaER9Fuzz_DataEEEE6selectEhS2_ Line | Count | Source | 164 | 232 | { | 165 | 232 | if (selector == sizeof...(Args)) { | 166 | 231 | return Arg(input); | 167 | 231 | } | 168 | 1 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 232 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_117TestModListUnpackER9Fuzz_DataEEXadL_ZNS0_23TestSanctionsListUnpackES2_EEXadL_ZNS0_23TestSanctionCredsUnpackES2_EEEE6selectEhS2_ Line | Count | Source | 164 | 30 | { | 165 | 30 | if (selector == sizeof...(Args)) { | 166 | 12 | return Arg(input); | 167 | 12 | } | 168 | 18 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 30 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_123TestSanctionsListUnpackER9Fuzz_DataEEXadL_ZNS0_23TestSanctionCredsUnpackES2_EEEE6selectEhS2_ Line | Count | Source | 164 | 18 | { | 165 | 18 | if (selector == sizeof...(Args)) { | 166 | 15 | return Arg(input); | 167 | 15 | } | 168 | 3 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 18 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_123TestSanctionCredsUnpackER9Fuzz_DataEEEE6selectEhS2_ Line | Count | Source | 164 | 3 | { | 165 | 3 | if (selector == sizeof...(Args)) { | 166 | 2 | return Arg(input); | 167 | 2 | } | 168 | 1 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 3 | } |
unity_0_cxx.cxx:_ZN20Fuzz_Target_SelectorIJXadL_ZN12_GLOBAL__N_113TestNetCryptoER9Fuzz_DataEEEE6selectEhS2_ Line | Count | Source | 164 | 20 | { | 165 | 20 | if (selector == sizeof...(Args)) { | 166 | 19 | return Arg(input); | 167 | 19 | } | 168 | 1 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 169 | 20 | } |
|
170 | | }; |
171 | | |
172 | | template <> |
173 | | struct Fuzz_Target_Selector<> { |
174 | | static void select(uint8_t selector, Fuzz_Data &input) |
175 | 5 | { |
176 | | // The selector selected no function, so we do nothing and rely on the |
177 | | // fuzzer to come up with a better selector. |
178 | 5 | } |
179 | | }; |
180 | | |
181 | | template <Fuzz_Target... Args> |
182 | | void fuzz_select_target(const uint8_t *data, std::size_t size) |
183 | 483 | { |
184 | 483 | Fuzz_Data input{data, size}; |
185 | | |
186 | 483 | CONSUME1_OR_RETURN(const uint8_t, selector, input); |
187 | 483 | return Fuzz_Target_Selector<Args...>::select(selector, input); |
188 | 483 | } unity_0_cxx.cxx:_Z18fuzz_select_targetIJXadL_ZN12_GLOBAL__N_117TestHandleRequestER9Fuzz_DataEEXadL_ZNS0_15TestUnpackNodesES2_EEEEvPKhm Line | Count | Source | 183 | 41 | { | 184 | 41 | Fuzz_Data input{data, size}; | 185 | | | 186 | 41 | CONSUME1_OR_RETURN(const uint8_t, selector, input); | 187 | 41 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 188 | 41 | } |
unity_0_cxx.cxx:_Z18fuzz_select_targetIJXadL_ZN12_GLOBAL__N_122TestSendForwardRequestER9Fuzz_DataEEXadL_ZNS0_16TestForwardReplyES2_EEEEvPKhm Line | Count | Source | 183 | 53 | { | 184 | 53 | Fuzz_Data input{data, size}; | 185 | | | 186 | 53 | CONSUME1_OR_RETURN(const uint8_t, selector, input); | 187 | 53 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 188 | 53 | } |
unity_0_cxx.cxx:_Z18fuzz_select_targetIJXadL_ZN12_GLOBAL__N_123TestUnpackAnnouncesListER9Fuzz_DataEEXadL_ZNS0_24TestUnpackPublicAnnounceES2_EEXadL_ZNS0_9TestDoGcaES2_EEEEvPKhm Line | Count | Source | 183 | 339 | { | 184 | 339 | Fuzz_Data input{data, size}; | 185 | | | 186 | 339 | CONSUME1_OR_RETURN(const uint8_t, selector, input); | 187 | 339 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 188 | 339 | } |
unity_0_cxx.cxx:_Z18fuzz_select_targetIJXadL_ZN12_GLOBAL__N_117TestModListUnpackER9Fuzz_DataEEXadL_ZNS0_23TestSanctionsListUnpackES2_EEXadL_ZNS0_23TestSanctionCredsUnpackES2_EEEEvPKhm Line | Count | Source | 183 | 30 | { | 184 | 30 | Fuzz_Data input{data, size}; | 185 | | | 186 | 30 | CONSUME1_OR_RETURN(const uint8_t, selector, input); | 187 | 30 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 188 | 30 | } |
unity_0_cxx.cxx:_Z18fuzz_select_targetIJXadL_ZN12_GLOBAL__N_113TestNetCryptoER9Fuzz_DataEEEEvPKhm Line | Count | Source | 183 | 20 | { | 184 | 20 | Fuzz_Data input{data, size}; | 185 | | | 186 | 20 | CONSUME1_OR_RETURN(const uint8_t, selector, input); | 187 | 20 | return Fuzz_Target_Selector<Args...>::select(selector, input); | 188 | 20 | } |
|
189 | | |
190 | | struct Tox_Memory; |
191 | | struct Network; |
192 | | struct Tox_Random; |
193 | | |
194 | | struct System { |
195 | | /** @brief Deterministic system clock for this instance. |
196 | | * |
197 | | * Different instances can evolve independently. The time is initialised |
198 | | * with a large number, because otherwise many zero-initialised "empty" |
199 | | * friends inside toxcore will be "not timed out" for a long time, messing |
200 | | * up some logic. Tox moderately depends on the clock being fairly high up |
201 | | * (not close to 0). |
202 | | * |
203 | | * We make it a nice large round number so we can recognise it when debugging. |
204 | | */ |
205 | | uint64_t clock = 1000000000; |
206 | | |
207 | | std::unique_ptr<Tox_System> sys; |
208 | | std::unique_ptr<Tox_Memory> mem; |
209 | | std::unique_ptr<Network> ns; |
210 | | std::unique_ptr<Tox_Random> rng; |
211 | | |
212 | | System(std::unique_ptr<Tox_System> sys, std::unique_ptr<Tox_Memory> mem, |
213 | | std::unique_ptr<Network> ns, std::unique_ptr<Tox_Random> rng); |
214 | | System(System &&); |
215 | | |
216 | | // Not inline because sizeof of the above 2 structs is not known everywhere. |
217 | | ~System(); |
218 | | |
219 | | /** |
220 | | * During bootstrap, move the time forward a decent amount, because friend |
221 | | * finding and bootstrapping takes significant (around 10 seconds) wall |
222 | | * clock time that should be advanced more quickly in the test. |
223 | | */ |
224 | | static constexpr uint8_t BOOTSTRAP_ITERATION_INTERVAL = 200; |
225 | | /** |
226 | | * Less than BOOTSTRAP_ITERATION_INTERVAL because otherwise we'll spam |
227 | | * onion announce packets. |
228 | | */ |
229 | | static constexpr uint8_t MESSAGE_ITERATION_INTERVAL = 20; |
230 | | /** |
231 | | * Move the clock forward at least 20ms so at least some amount of |
232 | | * time passes on each iteration. |
233 | | */ |
234 | | static constexpr uint8_t MIN_ITERATION_INTERVAL = 20; |
235 | | }; |
236 | | |
237 | | /** |
238 | | * A Tox_System implementation that consumes fuzzer input to produce network |
239 | | * inputs and random numbers. Once it runs out of fuzzer input, network receive |
240 | | * functions return no more data and the random numbers are always zero. |
241 | | */ |
242 | | struct Fuzz_System : System { |
243 | | Fuzz_Data &data; |
244 | | |
245 | | explicit Fuzz_System(Fuzz_Data &input); |
246 | | }; |
247 | | |
248 | | /** |
249 | | * A Tox_System implementation that consumes no fuzzer input but still has a |
250 | | * working and deterministic RNG. Network receive functions always fail, send |
251 | | * always succeeds. |
252 | | */ |
253 | | struct Null_System : System { |
254 | | uint64_t seed = 4; // chosen by fair dice roll. guaranteed to be random. |
255 | | |
256 | | Null_System(); |
257 | | }; |
258 | | |
259 | | template <typename V> |
260 | | class int_map { |
261 | | public: |
262 | | struct iterator { |
263 | | std::pair<uint16_t, V> pair; |
264 | | |
265 | | bool operator==(const iterator &rhs) const { return pair.first == rhs.pair.first; } |
266 | 0 | bool operator!=(const iterator &rhs) const { return pair.first != rhs.pair.first; } |
267 | | |
268 | | std::pair<uint16_t, V> operator*() const { return pair; } |
269 | 0 | const std::pair<uint16_t, V> *operator->() const { return &pair; } |
270 | | }; |
271 | | |
272 | | int_map() = default; |
273 | | ~int_map() = default; |
274 | | |
275 | | iterator find(uint16_t key) const |
276 | 0 | { |
277 | 0 | if (!values[key]) { |
278 | 0 | return end(); |
279 | 0 | } |
280 | 0 | return {{key, values[key]}}; |
281 | 0 | } |
282 | | |
283 | 0 | iterator end() const { return {{static_cast<uint16_t>(values.size()), nullptr}}; } |
284 | | |
285 | 0 | void emplace(uint16_t key, V value) { values[key] = value; } |
286 | | |
287 | | private: |
288 | | std::array<V, UINT16_MAX> values; |
289 | | }; |
290 | | |
291 | | /** |
292 | | * A Tox_System implementation that records all I/O but does not actually |
293 | | * perform any real I/O. Everything inside this system is hermetic in-process |
294 | | * and fully deterministic. |
295 | | * |
296 | | * Note: take care not to initialise two systems with the same seed, since |
297 | | * that's the only thing distinguishing the system's behaviour. Two toxes |
298 | | * initialised with the same seed will be identical (same keys, etc.). |
299 | | */ |
300 | | struct Record_System : System { |
301 | | static constexpr bool FUZZ_DEBUG = Fuzz_Data::FUZZ_DEBUG; |
302 | | |
303 | | /** @brief State shared between all tox instances. */ |
304 | | struct Global { |
305 | | /** @brief Bound UDP ports and their system instance. |
306 | | * |
307 | | * This implements an in-process network where instances can send |
308 | | * packets to other instances by inserting them into the receiver's |
309 | | * recvq using the receive function. |
310 | | * |
311 | | * We need to keep track of ports associated with recv queues because |
312 | | * toxcore sends packets to itself sometimes when doing onion routing |
313 | | * with only 2 nodes in the network. |
314 | | */ |
315 | | int_map<Record_System *> bound; |
316 | | }; |
317 | | |
318 | | Global &global_; |
319 | | uint64_t seed_; //!< Current PRNG state. |
320 | | const char *name_; //!< Tox system name ("tox1"/"tox2") for logging. |
321 | | |
322 | | std::deque<std::pair<uint16_t, std::vector<uint8_t>>> recvq; |
323 | | uint16_t port = 0; //!< Sending port for this system instance. |
324 | | |
325 | | Record_System(Global &global, uint64_t seed, const char *name); |
326 | | Record_System(const Record_System &) = delete; |
327 | | Record_System operator=(const Record_System &) = delete; |
328 | | |
329 | | /** @brief Deposit a network packet in this instance's recvq. |
330 | | */ |
331 | | void receive(uint16_t send_port, const uint8_t *buf, size_t len); |
332 | | |
333 | | void push(bool byte) |
334 | 0 | { |
335 | 0 | if (FUZZ_DEBUG) { |
336 | 0 | if (recording_.size() == Fuzz_Data::TRACE_TRAP) { |
337 | 0 | __asm__("int $3"); |
338 | 0 | } |
339 | 0 | std::printf( |
340 | 0 | "%s: produce@%zu(bool %s)\n", name_, recording_.size(), byte ? "true" : "false"); |
341 | 0 | } |
342 | 0 | recording_.push_back(byte); |
343 | 0 | } |
344 | | |
345 | | void push(uint8_t byte) |
346 | 0 | { |
347 | 0 | if (FUZZ_DEBUG) { |
348 | 0 | if (recording_.size() == Fuzz_Data::TRACE_TRAP) { |
349 | 0 | __asm__("int $3"); |
350 | 0 | } |
351 | 0 | std::printf("%s: produce@%zu(%u (0x%02x))\n", name_, recording_.size(), byte, byte); |
352 | 0 | } |
353 | 0 | recording_.push_back(byte); |
354 | 0 | } |
355 | | |
356 | | void push(const uint8_t *bytes, std::size_t size) |
357 | 0 | { |
358 | 0 | if (FUZZ_DEBUG) { |
359 | 0 | if (recording_.size() == Fuzz_Data::TRACE_TRAP) { |
360 | 0 | __asm__("int $3"); |
361 | 0 | } |
362 | 0 | std::printf("%s: produce@%zu(%02x..%02x[%zu])\n", name_, recording_.size(), bytes[0], |
363 | 0 | bytes[size - 1], size); |
364 | 0 | } |
365 | 0 | recording_.insert(recording_.end(), bytes, bytes + size); |
366 | 0 | } |
367 | | |
368 | | template <std::size_t N> |
369 | | void push(const char (&bytes)[N]) |
370 | 0 | { |
371 | 0 | push(reinterpret_cast<const uint8_t *>(bytes), N - 1); |
372 | 0 | } |
373 | | |
374 | 0 | const std::vector<uint8_t> &recording() const { return recording_; } |
375 | 0 | std::vector<uint8_t> take_recording() const { return std::move(recording_); } |
376 | | |
377 | | private: |
378 | | std::vector<uint8_t> recording_; |
379 | | }; |
380 | | |
381 | | /** @brief Enable debug logging. |
382 | | * |
383 | | * This should not be enabled in fuzzer code while fuzzing, as console I/O slows |
384 | | * everything down drastically. It's useful while developing the fuzzer and the |
385 | | * protodump program. |
386 | | */ |
387 | | extern const bool FUZZ_DEBUG; |
388 | | |
389 | | inline constexpr char tox_log_level_name(Tox_Log_Level level) |
390 | 0 | { |
391 | 0 | switch (level) { |
392 | 0 | case TOX_LOG_LEVEL_TRACE: |
393 | 0 | return 'T'; |
394 | 0 | case TOX_LOG_LEVEL_DEBUG: |
395 | 0 | return 'D'; |
396 | 0 | case TOX_LOG_LEVEL_INFO: |
397 | 0 | return 'I'; |
398 | 0 | case TOX_LOG_LEVEL_WARNING: |
399 | 0 | return 'W'; |
400 | 0 | case TOX_LOG_LEVEL_ERROR: |
401 | 0 | return 'E'; |
402 | 0 | } |
403 | 0 |
|
404 | 0 | return '?'; |
405 | 0 | } |
406 | | |
407 | | #endif // C_TOXCORE_TESTING_FUZZING_FUZZ_SUPPORT_H |