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