/src/haproxy/include/haproxy/bug.h
Line | Count | Source |
1 | | /* |
2 | | * include/haproxy/bug.h |
3 | | * Assertions and instant crash macros needed everywhere. |
4 | | * |
5 | | * Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining |
8 | | * a copy of this software and associated documentation files (the |
9 | | * "Software"), to deal in the Software without restriction, including |
10 | | * without limitation the rights to use, copy, modify, merge, publish, |
11 | | * distribute, sublicense, and/or sell copies of the Software, and to |
12 | | * permit persons to whom the Software is furnished to do so, subject to |
13 | | * the following conditions: |
14 | | * |
15 | | * The above copyright notice and this permission notice shall be |
16 | | * included in all copies or substantial portions of the Software. |
17 | | * |
18 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
19 | | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
20 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
21 | | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
22 | | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
23 | | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
24 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
25 | | * OTHER DEALINGS IN THE SOFTWARE. |
26 | | */ |
27 | | |
28 | | #ifndef _HAPROXY_BUG_H |
29 | | #define _HAPROXY_BUG_H |
30 | | |
31 | | #include <stddef.h> |
32 | | #include <sys/types.h> |
33 | | #include <haproxy/atomic.h> |
34 | | #include <haproxy/compiler.h> |
35 | | |
36 | | /* quick debugging hack, should really be removed ASAP */ |
37 | | #ifdef DEBUG_FULL |
38 | | #define DPRINTF(x...) fprintf(x) |
39 | | #else |
40 | | #define DPRINTF(x...) |
41 | | #endif |
42 | | |
43 | | /* Let's make DEBUG_STRESS equal to zero if not set or not valid, or to |
44 | | * 1 if set. This way it is always set and should be easy to use in "if ()" |
45 | | * statements without requiring ifdefs, while remaining compatible with |
46 | | * "#if DEBUG_STRESS > 0". We also force DEBUG_STRICT and DEBUG_STRICT_ACTION |
47 | | * when stressed. |
48 | | */ |
49 | | #if !defined(DEBUG_STRESS) |
50 | | # define DEBUG_STRESS 0 |
51 | | #elif DEBUG_STRESS != 0 |
52 | | # undef DEBUG_STRESS |
53 | | # define DEBUG_STRESS 1 // make sure comparison >0 always works |
54 | | # undef DEBUG_STRICT |
55 | | # define DEBUG_STRICT 2 // enable BUG_ON |
56 | | # undef DEBUG_STRICT_ACTION |
57 | | # define DEBUG_STRICT_ACTION 3 // enable crash on match |
58 | | #endif |
59 | | |
60 | 0 | #define DUMP_TRACE() do { extern void ha_backtrace_to_stderr(void); ha_backtrace_to_stderr(); } while (0) |
61 | | |
62 | | /* First, let's try to handle some arch-specific crashing methods. We prefer |
63 | | * the macro to the function because when opening the core, the debugger will |
64 | | * directly show the calling point (e.g. the BUG_ON() condition) based on the |
65 | | * line number, while the function will create new line numbers. But the |
66 | | * function is needed e.g. if some pragmas are needed. |
67 | | */ |
68 | | |
69 | | #if defined(__i386__) || defined(__x86_64__) |
70 | 0 | #define ha_crash_now() do { \ |
71 | 0 | /* ud2 opcode: 2 bytes, raises illegal instruction */ \ |
72 | 0 | __asm__ volatile(".byte 0x0f,0x0b\n"); \ |
73 | 0 | DO_NOT_FOLD(); \ |
74 | 0 | my_unreachable(); \ |
75 | 0 | } while (0) |
76 | | |
77 | | #elif defined(__aarch64__) |
78 | | #define ha_crash_now() do { \ |
79 | | /* udf#imm16: 4 bytes (), raises illegal instruction */ \ |
80 | | __asm__ volatile(".byte 0x00,0x00,0x00,0x00\n"); \ |
81 | | DO_NOT_FOLD(); \ |
82 | | my_unreachable(); \ |
83 | | } while (0) |
84 | | |
85 | | #else // not x86 |
86 | | |
87 | | /* generic implementation, causes a segfault */ |
88 | | static inline __attribute((always_inline,noreturn,unused)) void ha_crash_now(void) |
89 | | { |
90 | | #if __GNUC_PREREQ__(5, 0) |
91 | | #pragma GCC diagnostic push |
92 | | #pragma GCC diagnostic ignored "-Warray-bounds" |
93 | | #if __GNUC_PREREQ__(6, 0) |
94 | | #pragma GCC diagnostic ignored "-Wnull-dereference" |
95 | | #endif |
96 | | #endif |
97 | | *(volatile char *)1 = 0; |
98 | | #if __GNUC_PREREQ__(5, 0) |
99 | | #pragma GCC diagnostic pop |
100 | | #endif |
101 | | DO_NOT_FOLD(); |
102 | | my_unreachable(); |
103 | | } |
104 | | |
105 | | #endif // end of arch-specific ha_crash_now() definitions |
106 | | |
107 | | |
108 | | /* ABORT_NOW() usually takes no argument and will cause the program to abort |
109 | | * exactly where it is. We prefer to emit an invalid instruction to preserve |
110 | | * all registers, but it may fall back to a regular abort depending on the |
111 | | * platform. An optional argument can be a message string that will cause |
112 | | * the emission of a message saying "ABORT at" followed by the file and line |
113 | | * number then that message followed by a final line feed. This can be helpful |
114 | | * in situations where the core cannot be retrieved for example. However it |
115 | | * will definitely cause the loss of some registers, so should be avoided when |
116 | | * not strictly necessary. |
117 | | */ |
118 | | #define ABORT_NOW(...) \ |
119 | 0 | _ABORT_NOW(__FILE__, __LINE__, __VA_ARGS__) |
120 | | |
121 | | #define _ABORT_NOW(file, line, ...) \ |
122 | 0 | __ABORT_NOW(file, line, __VA_ARGS__) |
123 | | |
124 | | #ifdef DEBUG_USE_ABORT |
125 | | /* abort() is better recognized by code analysis tools */ |
126 | | |
127 | | /* abort() is generally tagged noreturn, so there's no 100% safe way to prevent |
128 | | * the compiler from doing a tail-merge here. Tests show that stopping folding |
129 | | * just before calling abort() does work in practice at -O2, increasing the |
130 | | * number of abort() calls in h3.o from 18 to 26, probably because there's no |
131 | | * more savings to be made by replacing a call with a jump. However, as -Os it |
132 | | * drops to 5 regardless of the build option. In order to help here, instead we |
133 | | * wrap abort() into another function, with the line number stored into a local |
134 | | * variable on the stack and we pretend to use it, so that unwinding the stack |
135 | | * from abort() will reveal its value even if the call was folded. |
136 | | */ |
137 | | static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line) |
138 | | { |
139 | | DISGUISE(&line); |
140 | | abort(); |
141 | | } |
142 | | |
143 | | #define __ABORT_NOW(file, line, ...) do { \ |
144 | | extern ssize_t write(int, const void *, size_t); \ |
145 | | extern size_t strlen(const char *s); \ |
146 | | const char *msg; \ |
147 | | if (sizeof("" __VA_ARGS__) > 1) \ |
148 | | complain(NULL, "\nABORT at " file ":" #line ": " __VA_ARGS__ "\n", 1); \ |
149 | | DUMP_TRACE(); \ |
150 | | msg = "\n" \ |
151 | | "Hint: when reporting this bug to developers, please check if a core file was\n" \ |
152 | | " produced, open it with 'gdb', issue 'bt' to produce a backtrace for the\n" \ |
153 | | " current thread only, then join it with the bug report.\n"; \ |
154 | | DISGUISE(write(2, msg, strlen(msg))); \ |
155 | | abort_with_line(__LINE__); \ |
156 | | } while (0) |
157 | | #else |
158 | | /* More efficient than abort() because it does not mangle the |
159 | | * stack and stops at the exact location we need. |
160 | | */ |
161 | 0 | #define __ABORT_NOW(file, line, ...) do { \ |
162 | 0 | extern ssize_t write(int, const void *, size_t); \ |
163 | 0 | extern size_t strlen(const char *s); \ |
164 | 0 | const char *msg; \ |
165 | 0 | if (sizeof("" __VA_ARGS__) > 1) \ |
166 | 0 | complain(NULL, "\nABORT at " file ":" #line ": " __VA_ARGS__ "\n", 1); \ |
167 | 0 | DUMP_TRACE(); \ |
168 | 0 | msg = "\n" \ |
169 | 0 | "Hint: when reporting this bug to developers, please check if a core file was\n" \ |
170 | 0 | " produced, open it with 'gdb', issue 'bt' to produce a backtrace for the\n" \ |
171 | 0 | " current thread only, then join it with the bug report.\n"; \ |
172 | 0 | DISGUISE(write(2, msg, strlen(msg))); \ |
173 | 0 | ha_crash_now(); \ |
174 | 0 | } while (0) |
175 | | #endif |
176 | | |
177 | | /* Counting the number of matches on a given BUG_ON()/WARN_ON()/CHECK_IF()/ |
178 | | * COUNT_IF() invocation requires a special section ("dbg_cnt") hence a modern |
179 | | * linker. |
180 | | */ |
181 | | extern unsigned int debug_enable_counters; |
182 | | |
183 | | #if !defined(USE_OBSOLETE_LINKER) |
184 | | |
185 | | /* type of checks that can be verified. We cannot really distinguish between |
186 | | * BUG/WARN/CHECK_IF as they all pass through __BUG_ON() at a different level, |
187 | | * but there's at least a difference between __BUG_ON() and __BUG_ON_ONCE() |
188 | | * (and of course COUNT_IF). |
189 | | */ |
190 | | enum debug_counter_type { |
191 | | DBG_BUG, |
192 | | DBG_BUG_ONCE, |
193 | | DBG_COUNT_IF, |
194 | | DBG_GLITCH, |
195 | | DBG_COUNTER_TYPES // must be last |
196 | | }; |
197 | | |
198 | | /* this is the struct that we store in section "dbg_cnt". Better keep it |
199 | | * well aligned. |
200 | | */ |
201 | | struct debug_count { |
202 | | const char *file; |
203 | | const char *func; |
204 | | const char *desc; |
205 | | uint16_t line; |
206 | | uint8_t type; |
207 | | /* one-byte hole here */ |
208 | | uint32_t count; |
209 | | }; |
210 | | |
211 | | /* Declare a section for condition counters. The start and stop pointers are |
212 | | * set by the linker itself, which is why they're declared extern here. The |
213 | | * weak attribute is used so that we declare them ourselves if the section is |
214 | | * empty. The corresponding section must contain exclusively struct debug_count |
215 | | * to make sure each location may safely be visited by incrementing a pointer. |
216 | | */ |
217 | | extern __attribute__((__weak__)) struct debug_count __start_dbg_cnt HA_SECTION_START("dbg_cnt"); |
218 | | extern __attribute__((__weak__)) struct debug_count __stop_dbg_cnt HA_SECTION_STOP("dbg_cnt"); |
219 | | |
220 | | /* This macro adds a pass counter at the line where it's declared. It can be |
221 | | * used by the various BUG_ON, COUNT_IF etc flavors. The condition is only |
222 | | * passed for the sake of being turned into a string; the caller is expected |
223 | | * to have already verified it. |
224 | | */ |
225 | 0 | #define __DBG_COUNT(_cond, _file, _line, _type, ...) do { \ |
226 | 0 | static struct debug_count __dbg_cnt_##_line HA_SECTION("dbg_cnt") \ |
227 | 0 | __attribute__((__used__,__aligned__(sizeof(void*)))) = { \ |
228 | 0 | .file = _file, \ |
229 | 0 | .func = __func__, \ |
230 | 0 | .line = _line, \ |
231 | 0 | .type = _type, \ |
232 | 0 | .desc = (sizeof("" #_cond) > 1) ? \ |
233 | 0 | (sizeof("" __VA_ARGS__) > 1) ? \ |
234 | 0 | "\"" #_cond "\" [" __VA_ARGS__ "]" : \ |
235 | 0 | "\"" #_cond "\"" : \ |
236 | 0 | "" __VA_ARGS__, \ |
237 | 0 | .count = 0, \ |
238 | 0 | }; \ |
239 | 0 | HA_WEAK(__start_dbg_cnt); \ |
240 | 0 | HA_WEAK(__stop_dbg_cnt); \ |
241 | 0 | _HA_ATOMIC_INC(&__dbg_cnt_##_line.count); \ |
242 | 0 | } while (0) |
243 | | |
244 | | |
245 | | /* Matrix for DEBUG_COUNTERS: |
246 | | * 0 : only BUG_ON() and CHECK_IF() are reported (super rare) |
247 | | * 1 : COUNT_GLITCH() are also reported (rare) |
248 | | * COUNT_IF() are also reported only if debug_enable_counters was set |
249 | | * 2 : COUNT_IF() are also reported unless debug_enable_counters was reset |
250 | | */ |
251 | | |
252 | | /* Core of the COUNT_IF() macro, checks the condition and counts one hit if |
253 | | * true. It's only enabled at DEBUG_COUNTERS >= 1, and enabled by default if |
254 | | * DEBUG_COUNTERS >= 2. |
255 | | */ |
256 | | # if defined(DEBUG_COUNTERS) && (DEBUG_COUNTERS >= 1) |
257 | | # define _COUNT_IF(cond, file, line, ...) \ |
258 | 0 | (unlikely(cond) ? ({ \ |
259 | 0 | if (debug_enable_counters) \ |
260 | 0 | __DBG_COUNT(cond, file, line, DBG_COUNT_IF, __VA_ARGS__); \ |
261 | 0 | 1; /* let's return the true condition */ \ |
262 | 0 | }) : 0) |
263 | | # else |
264 | | # define _COUNT_IF(cond, file, line, ...) DISGUISE(unlikely(cond) ? 1 : 0) |
265 | | # endif |
266 | | |
267 | | /* DEBUG_COUNTERS enables counting the number of glitches per line of code. The |
268 | | * condition is empty (nothing to write there), except maybe __VA_ARGS at the |
269 | | * end. |
270 | | */ |
271 | | # if !defined(DEBUG_COUNTERS) || (DEBUG_COUNTERS == 0) |
272 | | # define _COUNT_GLITCH(file, line, ...) do { } while (0) |
273 | | # else |
274 | | # define _COUNT_GLITCH(file, line, ...) do { \ |
275 | | __DBG_COUNT(, file, line, DBG_GLITCH, __VA_ARGS__); \ |
276 | | } while (0) |
277 | | # endif |
278 | | |
279 | | #else /* USE_OBSOLETE_LINKER not defined below */ |
280 | | # define __DBG_COUNT(cond, file, line, type, ...) do { } while (0) |
281 | | # define _COUNT_IF(cond, file, line, ...) DISGUISE(unlikely(cond) ? 1 : 0) |
282 | | # define _COUNT_GLITCH(file, line, ...) do { } while (0) |
283 | | #endif |
284 | | |
285 | | /* reports a glitch for current file and line, optionally with an explanation */ |
286 | | #define COUNT_GLITCH(...) _COUNT_GLITCH(__FILE__, __LINE__, __VA_ARGS__) |
287 | | |
288 | | /* This is the generic low-level macro dealing with conditional warnings and |
289 | | * bugs. The caller decides whether to crash or not and what prefix and suffix |
290 | | * to pass. The macro returns the boolean value of the condition as an int for |
291 | | * the case where it wouldn't die. The <crash> flag is made of: |
292 | | * - crash & 1: crash yes/no; |
293 | | * - crash & 2: taint as bug instead of warn |
294 | | * The optional argument must be a single constant string that will be appended |
295 | | * on a second line after the condition message, to give a bit more context |
296 | | * about the problem. |
297 | | */ |
298 | | #define _BUG_ON(cond, file, line, crash, pfx, sfx, ...) \ |
299 | 12 | (void)(unlikely(cond) ? ({ \ |
300 | 0 | __DBG_COUNT(cond, file, line, DBG_BUG, __VA_ARGS__); \ |
301 | 0 | __BUG_ON(cond, file, line, crash, pfx, sfx, __VA_ARGS__); \ |
302 | 0 | 1; /* let's return the true condition */ \ |
303 | 12 | }) : 0) |
304 | | |
305 | 0 | #define __BUG_ON(cond, file, line, crash, pfx, sfx, ...) do { \ |
306 | 0 | const char *msg; \ |
307 | 0 | if (sizeof("" __VA_ARGS__) > 1) \ |
308 | 0 | msg ="\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n" __VA_ARGS__ "\n"; \ |
309 | 0 | else \ |
310 | 0 | msg = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \ |
311 | 0 | complain(NULL, msg, crash); \ |
312 | 0 | if (crash & 1) \ |
313 | 0 | ABORT_NOW(); \ |
314 | 0 | else \ |
315 | 0 | DUMP_TRACE(); \ |
316 | 0 | } while (0) |
317 | | |
318 | | /* This one is equivalent except that it only emits the message once by |
319 | | * maintaining a static counter. This may be used with warnings to detect |
320 | | * certain unexpected conditions in field. Later on, in cores it will be |
321 | | * possible to verify these counters. |
322 | | */ |
323 | | #define _BUG_ON_ONCE(cond, file, line, crash, pfx, sfx, ...) \ |
324 | 0 | (void)(unlikely(cond) ? ({ \ |
325 | 0 | __DBG_COUNT(cond, file, line, DBG_BUG_ONCE, __VA_ARGS__); \ |
326 | 0 | __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx, __VA_ARGS__); \ |
327 | 0 | 1; /* let's return the true condition */ \ |
328 | 0 | }) : 0) |
329 | | |
330 | 0 | #define __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx, ...) do { \ |
331 | 0 | static int __match_count_##line; \ |
332 | 0 | const char *msg; \ |
333 | 0 | if (sizeof("" __VA_ARGS__) > 1) \ |
334 | 0 | msg ="\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n" __VA_ARGS__ "\n"; \ |
335 | 0 | else \ |
336 | 0 | msg = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \ |
337 | 0 | complain(&__match_count_##line, msg, crash); \ |
338 | 0 | if (crash & 1) \ |
339 | 0 | ABORT_NOW(); \ |
340 | 0 | else \ |
341 | 0 | DUMP_TRACE(); \ |
342 | 0 | } while (0) |
343 | | |
344 | | |
345 | | /* DEBUG_STRICT enables/disables runtime checks on condition <cond> |
346 | | * DEBUG_STRICT_ACTION indicates the level of verification on the rules when |
347 | | * <cond> is true: |
348 | | * |
349 | | * macro BUG_ON() WARN_ON() CHECK_IF() |
350 | | * value 0 warn warn warn |
351 | | * 1 CRASH warn warn |
352 | | * 2 CRASH CRASH warn |
353 | | * 3 CRASH CRASH CRASH |
354 | | */ |
355 | | |
356 | | /* The macros below are for general use */ |
357 | | #if defined(DEBUG_STRICT) && (DEBUG_STRICT > 0) |
358 | | # if defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION < 1) |
359 | | /* Lowest level: BUG_ON() warns, WARN_ON() warns, CHECK_IF() warns */ |
360 | | # define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 2, "WARNING: bug ", " (not crashing but process is untrusted now, please report to developers)", __VA_ARGS__) |
361 | | # define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: warn ", " (please report to developers)", __VA_ARGS__) |
362 | | # define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__) |
363 | | # define COUNT_IF(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
364 | | # elif !defined(DEBUG_STRICT_ACTION) || (DEBUG_STRICT_ACTION == 1) |
365 | | /* default level: BUG_ON() crashes, WARN_ON() warns, CHECK_IF() warns */ |
366 | 12 | # define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__) |
367 | 0 | # define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: warn ", " (please report to developers)", __VA_ARGS__) |
368 | 0 | # define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__) |
369 | 0 | # define COUNT_IF(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
370 | | # elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION == 2) |
371 | | /* Stricter level: BUG_ON() crashes, WARN_ON() crashes, CHECK_IF() warns */ |
372 | | # define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__) |
373 | | # define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: warn ", "", __VA_ARGS__) |
374 | | # define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__) |
375 | | # define COUNT_IF(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
376 | | # elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION >= 3) |
377 | | /* Developer/CI level: BUG_ON() crashes, WARN_ON() crashes, CHECK_IF() crashes */ |
378 | | # define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__) |
379 | | # define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: warn ", "", __VA_ARGS__) |
380 | | # define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 1, "FATAL: check ", "", __VA_ARGS__) |
381 | | # define COUNT_IF(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
382 | | # endif |
383 | | #else |
384 | | /* We want BUG_ON() to evaluate the expression sufficiently for next lines |
385 | | * of codes not to complain about suspicious dereferences for example. |
386 | | * GCC-11 tends to fail to validate that in combined expressions such as |
387 | | * "BUG_ON(!a || !b)", but it works fine when using a temporary assignment |
388 | | * like below, without hurting the generated code. |
389 | | */ |
390 | | # define BUG_ON(cond, ...) ({ typeof(cond) __cond = (cond); ASSUME(!__cond); }) |
391 | | # define WARN_ON(cond, ...) do { (void)sizeof(cond); } while (0) |
392 | | # define CHECK_IF(cond, ...) do { (void)sizeof(cond); } while (0) |
393 | | # define COUNT_IF(cond, ...) DISGUISE(cond) |
394 | | #endif |
395 | | |
396 | | /* These macros are only for hot paths and remain disabled unless DEBUG_STRICT is 2 or above. |
397 | | * Only developers/CI should use these levels as they may significantly impact performance by |
398 | | * enabling checks in sensitive areas. |
399 | | */ |
400 | | #if defined(DEBUG_STRICT) && (DEBUG_STRICT > 1) |
401 | | # if defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION < 1) |
402 | | /* Lowest level: BUG_ON() warns, CHECK_IF() warns */ |
403 | | # define BUG_ON_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 2, "WARNING: bug ", " (not crashing but process is untrusted now, please report to developers)", __VA_ARGS__) |
404 | | # define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__) |
405 | | # define COUNT_IF_HOT(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
406 | | # elif !defined(DEBUG_STRICT_ACTION) || (DEBUG_STRICT_ACTION < 3) |
407 | | /* default level: BUG_ON() crashes, CHECK_IF() warns */ |
408 | | # define BUG_ON_HOT(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__) |
409 | | # define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__) |
410 | | # define COUNT_IF_HOT(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
411 | | # elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION >= 3) |
412 | | /* Developer/CI level: BUG_ON() crashes, CHECK_IF() crashes */ |
413 | | # define BUG_ON_HOT(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__) |
414 | | # define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 1, "FATAL: check ", "", __VA_ARGS__) |
415 | | # define COUNT_IF_HOT(cond, ...) _COUNT_IF (cond, __FILE__, __LINE__, __VA_ARGS__) |
416 | | # endif |
417 | | #else |
418 | | /* Contrary to BUG_ON(), we do *NOT* want BUG_ON_HOT() to evaluate the |
419 | | * expression unless explicitly enabled, since it is located in hot code paths. |
420 | | * We just validate that the expression results in a valid type. |
421 | | */ |
422 | 38.3k | # define BUG_ON_HOT(cond, ...) do { (void)sizeof(cond) ; } while (0) |
423 | 0 | # define CHECK_IF_HOT(cond, ...) do { (void)sizeof(cond) ; } while (0) |
424 | | # define COUNT_IF_HOT(cond, ...) DISGUISE(cond) |
425 | | #endif |
426 | | |
427 | | /* turn BUG_ON_STRESS() into a real statement when DEBUG_STRESS is set, |
428 | | * otherwise simply ignore it, at the risk of failing to notice if the |
429 | | * condition would build at all. We don't really care if BUG_ON_STRESS |
430 | | * doesn't always build, because it's meant to be used only in certain |
431 | | * scenarios, possibly requiring certain combinations of options. We |
432 | | * just want to be certain that the condition is not implemented at all |
433 | | * when not used, so as to encourage developers to put a lot of them at |
434 | | * zero cost. |
435 | | */ |
436 | | #if DEBUG_STRESS > 0 |
437 | | # define BUG_ON_STRESS(cond, ...) BUG_ON(cond, __VA_ARGS__) |
438 | | #else |
439 | 0 | # define BUG_ON_STRESS(cond, ...) do { } while (0) |
440 | | #endif |
441 | | |
442 | | /* When not optimizing, clang won't remove that code, so only compile it in when optimizing */ |
443 | | #if defined(__GNUC__) && defined(__OPTIMIZE__) |
444 | | #define HA_LINK_ERROR(what) \ |
445 | 0 | do { \ |
446 | 0 | /* provoke a build-time error */ \ |
447 | 0 | extern volatile int what; \ |
448 | 0 | what = 1; \ |
449 | 0 | } while (0) |
450 | | #else |
451 | | #define HA_LINK_ERROR(what) \ |
452 | | do { \ |
453 | | } while (0) |
454 | | #endif /* __OPTIMIZE__ */ |
455 | | |
456 | | /* more reliable free() that clears the pointer */ |
457 | 4.76k | #define ha_free(x) do { \ |
458 | 4.76k | typeof(x) __x = (x); \ |
459 | 4.76k | if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) { \ |
460 | 0 | HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \ |
461 | 0 | } \ |
462 | 4.76k | free(*__x); \ |
463 | 4.76k | *__x = NULL; \ |
464 | 4.76k | } while (0) |
465 | | |
466 | | /* describes a call place in the code, for example for tracing memory |
467 | | * allocations or task wakeups. These must be declared static const. |
468 | | */ |
469 | | struct ha_caller { |
470 | | const char *func; // function name |
471 | | const char *file; // file name |
472 | | uint16_t line; // line number |
473 | | uint8_t what; // description of the call, usage specific |
474 | | uint8_t arg8; // optional argument, usage specific |
475 | | uint32_t arg32; // optional argument, usage specific |
476 | | }; |
477 | | |
478 | | #define MK_CALLER(_what, _arg8, _arg32) \ |
479 | 0 | ({ static const struct ha_caller _ = { \ |
480 | 0 | .func = __func__, .file = __FILE__, .line = __LINE__, \ |
481 | 0 | .what = _what, .arg8 = _arg8, .arg32 = _arg32 }; \ |
482 | 0 | &_; }) |
483 | | |
484 | | /* handle 'tainted' status */ |
485 | | enum tainted_flags { |
486 | | TAINTED_CONFIG_EXP_KW_DECLARED = 0x00000001, |
487 | | TAINTED_ACTION_EXP_EXECUTED = 0x00000002, |
488 | | TAINTED_CLI_EXPERT_MODE = 0x00000004, |
489 | | TAINTED_CLI_EXPERIMENTAL_MODE = 0x00000008, |
490 | | TAINTED_WARN = 0x00000010, /* a WARN_ON triggered */ |
491 | | TAINTED_BUG = 0x00000020, /* a BUG_ON triggered */ |
492 | | TAINTED_SHARED_LIBS = 0x00000040, /* a shared library was loaded */ |
493 | | TAINTED_REDEFINITION = 0x00000080, /* symbol redefinition detected */ |
494 | | TAINTED_REPLACED_MEM_ALLOCATOR = 0x00000100, /* memory allocator was replaced using LD_PRELOAD */ |
495 | | TAINTED_PANIC = 0x00000200, /* a panic dump has started */ |
496 | | TAINTED_LUA_STUCK = 0x00000400, /* stuck in a Lua context */ |
497 | | TAINTED_LUA_STUCK_SHARED = 0x00000800, /* stuck in a shared Lua context */ |
498 | | TAINTED_MEM_TRIMMING_STUCK = 0x00001000, /* stuck while trimming memory */ |
499 | | TAINTED_WARN_BLOCKED_TRAFFIC = 0x00002000, /* emitted a warning about blocked traffic */ |
500 | | }; |
501 | | |
502 | | /* this is a bit field made of TAINTED_*, and is declared in haproxy.c */ |
503 | | extern unsigned int tainted; |
504 | | |
505 | | void complain(int *counter, const char *msg, int taint); |
506 | | |
507 | | static inline unsigned int mark_tainted(const enum tainted_flags flag) |
508 | 0 | { |
509 | 0 | return HA_ATOMIC_FETCH_OR(&tainted, flag); |
510 | 0 | } Unexecuted instantiation: fuzz_hpack_decode.c:mark_tainted Unexecuted instantiation: fuzz_cfg_parser.c:mark_tainted Unexecuted instantiation: cfgparse.c:mark_tainted Unexecuted instantiation: cli.c:mark_tainted Unexecuted instantiation: clock.c:mark_tainted Unexecuted instantiation: connection.c:mark_tainted Unexecuted instantiation: debug.c:mark_tainted Unexecuted instantiation: dynbuf.c:mark_tainted Unexecuted instantiation: ebtree.c:mark_tainted Unexecuted instantiation: errors.c:mark_tainted Unexecuted instantiation: fd.c:mark_tainted Unexecuted instantiation: frontend.c:mark_tainted Unexecuted instantiation: haproxy.c:mark_tainted Unexecuted instantiation: http_ana.c:mark_tainted Unexecuted instantiation: http_ext.c:mark_tainted Unexecuted instantiation: http_htx.c:mark_tainted Unexecuted instantiation: http_rules.c:mark_tainted Unexecuted instantiation: htx.c:mark_tainted Unexecuted instantiation: init.c:mark_tainted Unexecuted instantiation: limits.c:mark_tainted Unexecuted instantiation: listener.c:mark_tainted Unexecuted instantiation: log.c:mark_tainted Unexecuted instantiation: mailers.c:mark_tainted Unexecuted instantiation: mworker.c:mark_tainted Unexecuted instantiation: peers.c:mark_tainted Unexecuted instantiation: pool.c:mark_tainted Unexecuted instantiation: proto_rhttp.c:mark_tainted Unexecuted instantiation: proto_sockpair.c:mark_tainted Unexecuted instantiation: protocol.c:mark_tainted Unexecuted instantiation: proxy.c:mark_tainted Unexecuted instantiation: queue.c:mark_tainted Unexecuted instantiation: regex.c:mark_tainted Unexecuted instantiation: resolvers.c:mark_tainted Unexecuted instantiation: ring.c:mark_tainted Unexecuted instantiation: sample.c:mark_tainted Unexecuted instantiation: server.c:mark_tainted Unexecuted instantiation: session.c:mark_tainted Unexecuted instantiation: signal.c:mark_tainted Unexecuted instantiation: sink.c:mark_tainted Unexecuted instantiation: sock.c:mark_tainted Unexecuted instantiation: sock_inet.c:mark_tainted Unexecuted instantiation: stats-html.c:mark_tainted Unexecuted instantiation: stats.c:mark_tainted Unexecuted instantiation: stconn.c:mark_tainted Unexecuted instantiation: stick_table.c:mark_tainted Unexecuted instantiation: stream.c:mark_tainted Unexecuted instantiation: systemd.c:mark_tainted Unexecuted instantiation: task.c:mark_tainted Unexecuted instantiation: tcp_rules.c:mark_tainted Unexecuted instantiation: tcpcheck.c:mark_tainted Unexecuted instantiation: thread.c:mark_tainted Unexecuted instantiation: time.c:mark_tainted Unexecuted instantiation: tools.c:mark_tainted Unexecuted instantiation: trace.c:mark_tainted Unexecuted instantiation: uri_auth.c:mark_tainted Unexecuted instantiation: vars.c:mark_tainted Unexecuted instantiation: version.c:mark_tainted Unexecuted instantiation: acl.c:mark_tainted Unexecuted instantiation: action.c:mark_tainted Unexecuted instantiation: activity.c:mark_tainted Unexecuted instantiation: applet.c:mark_tainted Unexecuted instantiation: arg.c:mark_tainted Unexecuted instantiation: auth.c:mark_tainted Unexecuted instantiation: backend.c:mark_tainted Unexecuted instantiation: base64.c:mark_tainted Unexecuted instantiation: buf.c:mark_tainted Unexecuted instantiation: cfgcond.c:mark_tainted Unexecuted instantiation: cfgparse-global.c:mark_tainted Unexecuted instantiation: cfgparse-listen.c:mark_tainted Unexecuted instantiation: channel.c:mark_tainted Unexecuted instantiation: check.c:mark_tainted Unexecuted instantiation: chunk.c:mark_tainted Unexecuted instantiation: compression.c:mark_tainted Unexecuted instantiation: counters.c:mark_tainted Unexecuted instantiation: dgram.c:mark_tainted Unexecuted instantiation: dict.c:mark_tainted Unexecuted instantiation: dns.c:mark_tainted Unexecuted instantiation: dns_ring.c:mark_tainted Unexecuted instantiation: eb32tree.c:mark_tainted Unexecuted instantiation: eb64tree.c:mark_tainted Unexecuted instantiation: ebimtree.c:mark_tainted Unexecuted instantiation: ebistree.c:mark_tainted Unexecuted instantiation: ebmbtree.c:mark_tainted Unexecuted instantiation: ebsttree.c:mark_tainted Unexecuted instantiation: event_hdl.c:mark_tainted Unexecuted instantiation: extcheck.c:mark_tainted Unexecuted instantiation: filters.c:mark_tainted Unexecuted instantiation: fix.c:mark_tainted Unexecuted instantiation: flt_http_comp.c:mark_tainted Unexecuted instantiation: freq_ctr.c:mark_tainted Unexecuted instantiation: guid.c:mark_tainted Unexecuted instantiation: h1.c:mark_tainted Unexecuted instantiation: haterm.c:mark_tainted Unexecuted instantiation: http.c:mark_tainted Unexecuted instantiation: http_fetch.c:mark_tainted Unexecuted instantiation: lb_chash.c:mark_tainted Unexecuted instantiation: lb_fas.c:mark_tainted Unexecuted instantiation: lb_fwlc.c:mark_tainted Unexecuted instantiation: lb_fwrr.c:mark_tainted Unexecuted instantiation: lb_map.c:mark_tainted Unexecuted instantiation: lb_ss.c:mark_tainted Unexecuted instantiation: mqtt.c:mark_tainted Unexecuted instantiation: mux_spop.c:mark_tainted Unexecuted instantiation: pattern.c:mark_tainted Unexecuted instantiation: payload.c:mark_tainted Unexecuted instantiation: pipe.c:mark_tainted Unexecuted instantiation: proto_tcp.c:mark_tainted Unexecuted instantiation: stats-file.c:mark_tainted Unexecuted instantiation: stats-json.c:mark_tainted Unexecuted instantiation: stats-proxy.c:mark_tainted Unexecuted instantiation: cache.c:mark_tainted Unexecuted instantiation: fcgi-app.c:mark_tainted Unexecuted instantiation: flt_spoe.c:mark_tainted Unexecuted instantiation: h1_htx.c:mark_tainted Unexecuted instantiation: lru.c:mark_tainted Unexecuted instantiation: shctx.c:mark_tainted Unexecuted instantiation: fuzz_h1_parse.c:mark_tainted |
511 | | |
512 | | static inline unsigned int get_tainted() |
513 | 0 | { |
514 | 0 | return HA_ATOMIC_LOAD(&tainted); |
515 | 0 | } Unexecuted instantiation: fuzz_hpack_decode.c:get_tainted Unexecuted instantiation: fuzz_cfg_parser.c:get_tainted Unexecuted instantiation: cfgparse.c:get_tainted Unexecuted instantiation: cli.c:get_tainted Unexecuted instantiation: clock.c:get_tainted Unexecuted instantiation: connection.c:get_tainted Unexecuted instantiation: debug.c:get_tainted Unexecuted instantiation: dynbuf.c:get_tainted Unexecuted instantiation: ebtree.c:get_tainted Unexecuted instantiation: errors.c:get_tainted Unexecuted instantiation: fd.c:get_tainted Unexecuted instantiation: frontend.c:get_tainted Unexecuted instantiation: haproxy.c:get_tainted Unexecuted instantiation: http_ana.c:get_tainted Unexecuted instantiation: http_ext.c:get_tainted Unexecuted instantiation: http_htx.c:get_tainted Unexecuted instantiation: http_rules.c:get_tainted Unexecuted instantiation: htx.c:get_tainted Unexecuted instantiation: init.c:get_tainted Unexecuted instantiation: limits.c:get_tainted Unexecuted instantiation: listener.c:get_tainted Unexecuted instantiation: log.c:get_tainted Unexecuted instantiation: mailers.c:get_tainted Unexecuted instantiation: mworker.c:get_tainted Unexecuted instantiation: peers.c:get_tainted Unexecuted instantiation: pool.c:get_tainted Unexecuted instantiation: proto_rhttp.c:get_tainted Unexecuted instantiation: proto_sockpair.c:get_tainted Unexecuted instantiation: protocol.c:get_tainted Unexecuted instantiation: proxy.c:get_tainted Unexecuted instantiation: queue.c:get_tainted Unexecuted instantiation: regex.c:get_tainted Unexecuted instantiation: resolvers.c:get_tainted Unexecuted instantiation: ring.c:get_tainted Unexecuted instantiation: sample.c:get_tainted Unexecuted instantiation: server.c:get_tainted Unexecuted instantiation: session.c:get_tainted Unexecuted instantiation: signal.c:get_tainted Unexecuted instantiation: sink.c:get_tainted Unexecuted instantiation: sock.c:get_tainted Unexecuted instantiation: sock_inet.c:get_tainted Unexecuted instantiation: stats-html.c:get_tainted Unexecuted instantiation: stats.c:get_tainted Unexecuted instantiation: stconn.c:get_tainted Unexecuted instantiation: stick_table.c:get_tainted Unexecuted instantiation: stream.c:get_tainted Unexecuted instantiation: systemd.c:get_tainted Unexecuted instantiation: task.c:get_tainted Unexecuted instantiation: tcp_rules.c:get_tainted Unexecuted instantiation: tcpcheck.c:get_tainted Unexecuted instantiation: thread.c:get_tainted Unexecuted instantiation: time.c:get_tainted Unexecuted instantiation: tools.c:get_tainted Unexecuted instantiation: trace.c:get_tainted Unexecuted instantiation: uri_auth.c:get_tainted Unexecuted instantiation: vars.c:get_tainted Unexecuted instantiation: version.c:get_tainted Unexecuted instantiation: acl.c:get_tainted Unexecuted instantiation: action.c:get_tainted Unexecuted instantiation: activity.c:get_tainted Unexecuted instantiation: applet.c:get_tainted Unexecuted instantiation: arg.c:get_tainted Unexecuted instantiation: auth.c:get_tainted Unexecuted instantiation: backend.c:get_tainted Unexecuted instantiation: base64.c:get_tainted Unexecuted instantiation: buf.c:get_tainted Unexecuted instantiation: cfgcond.c:get_tainted Unexecuted instantiation: cfgparse-global.c:get_tainted Unexecuted instantiation: cfgparse-listen.c:get_tainted Unexecuted instantiation: channel.c:get_tainted Unexecuted instantiation: check.c:get_tainted Unexecuted instantiation: chunk.c:get_tainted Unexecuted instantiation: compression.c:get_tainted Unexecuted instantiation: counters.c:get_tainted Unexecuted instantiation: dgram.c:get_tainted Unexecuted instantiation: dict.c:get_tainted Unexecuted instantiation: dns.c:get_tainted Unexecuted instantiation: dns_ring.c:get_tainted Unexecuted instantiation: eb32tree.c:get_tainted Unexecuted instantiation: eb64tree.c:get_tainted Unexecuted instantiation: ebimtree.c:get_tainted Unexecuted instantiation: ebistree.c:get_tainted Unexecuted instantiation: ebmbtree.c:get_tainted Unexecuted instantiation: ebsttree.c:get_tainted Unexecuted instantiation: event_hdl.c:get_tainted Unexecuted instantiation: extcheck.c:get_tainted Unexecuted instantiation: filters.c:get_tainted Unexecuted instantiation: fix.c:get_tainted Unexecuted instantiation: flt_http_comp.c:get_tainted Unexecuted instantiation: freq_ctr.c:get_tainted Unexecuted instantiation: guid.c:get_tainted Unexecuted instantiation: h1.c:get_tainted Unexecuted instantiation: haterm.c:get_tainted Unexecuted instantiation: http.c:get_tainted Unexecuted instantiation: http_fetch.c:get_tainted Unexecuted instantiation: lb_chash.c:get_tainted Unexecuted instantiation: lb_fas.c:get_tainted Unexecuted instantiation: lb_fwlc.c:get_tainted Unexecuted instantiation: lb_fwrr.c:get_tainted Unexecuted instantiation: lb_map.c:get_tainted Unexecuted instantiation: lb_ss.c:get_tainted Unexecuted instantiation: mqtt.c:get_tainted Unexecuted instantiation: mux_spop.c:get_tainted Unexecuted instantiation: pattern.c:get_tainted Unexecuted instantiation: payload.c:get_tainted Unexecuted instantiation: pipe.c:get_tainted Unexecuted instantiation: proto_tcp.c:get_tainted Unexecuted instantiation: stats-file.c:get_tainted Unexecuted instantiation: stats-json.c:get_tainted Unexecuted instantiation: stats-proxy.c:get_tainted Unexecuted instantiation: cache.c:get_tainted Unexecuted instantiation: fcgi-app.c:get_tainted Unexecuted instantiation: flt_spoe.c:get_tainted Unexecuted instantiation: h1_htx.c:get_tainted Unexecuted instantiation: lru.c:get_tainted Unexecuted instantiation: shctx.c:get_tainted Unexecuted instantiation: fuzz_h1_parse.c:get_tainted |
516 | | |
517 | | #if defined(DEBUG_MEM_STATS) |
518 | | #include <stdlib.h> |
519 | | #include <string.h> |
520 | | |
521 | | /* Memory allocation statistics are centralized into a global "mem_stats" |
522 | | * section. This will not work with some linkers. |
523 | | */ |
524 | | enum { |
525 | | MEM_STATS_TYPE_UNSET = 0, |
526 | | MEM_STATS_TYPE_CALLOC, |
527 | | MEM_STATS_TYPE_FREE, |
528 | | MEM_STATS_TYPE_MALLOC, |
529 | | MEM_STATS_TYPE_REALLOC, |
530 | | MEM_STATS_TYPE_STRDUP, |
531 | | MEM_STATS_TYPE_P_ALLOC, |
532 | | MEM_STATS_TYPE_P_FREE, |
533 | | }; |
534 | | |
535 | | struct mem_stats { |
536 | | size_t calls; |
537 | | size_t size; |
538 | | struct ha_caller caller; |
539 | | const void *extra; // extra info specific to this call (e.g. pool ptr) |
540 | | } ALIGNED(sizeof(void*)); |
541 | | |
542 | | #undef calloc |
543 | | #define calloc(x,y) ({ \ |
544 | | size_t __x = (x); size_t __y = (y); \ |
545 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
546 | | .caller = { \ |
547 | | .file = __FILE__, .line = __LINE__, \ |
548 | | .what = MEM_STATS_TYPE_CALLOC, \ |
549 | | .func = __func__, \ |
550 | | }, \ |
551 | | }; \ |
552 | | HA_WEAK(__start_mem_stats); \ |
553 | | HA_WEAK(__stop_mem_stats); \ |
554 | | _HA_ATOMIC_INC(&_.calls); \ |
555 | | _HA_ATOMIC_ADD(&_.size, __x * __y); \ |
556 | | calloc(__x,__y); \ |
557 | | }) |
558 | | |
559 | | /* note: we can't redefine free() because we have a few variables and struct |
560 | | * members called like this. This one may be used before a call to free(), |
561 | | * and when known, the size should be indicated, otherwise pass zero. The |
562 | | * pointer is used to know whether the call should be accounted for (null is |
563 | | * ignored). |
564 | | */ |
565 | | #undef will_free |
566 | | #define will_free(x, y) ({ \ |
567 | | void *__x = (x); size_t __y = (y); \ |
568 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
569 | | .caller = { \ |
570 | | .file = __FILE__, .line = __LINE__, \ |
571 | | .what = MEM_STATS_TYPE_FREE, \ |
572 | | .func = __func__, \ |
573 | | }, \ |
574 | | }; \ |
575 | | HA_WEAK(__start_mem_stats); \ |
576 | | HA_WEAK(__stop_mem_stats); \ |
577 | | if (__x) { \ |
578 | | _HA_ATOMIC_INC(&_.calls); \ |
579 | | _HA_ATOMIC_ADD(&_.size, __y); \ |
580 | | } \ |
581 | | }) |
582 | | |
583 | | #undef ha_free |
584 | | #define ha_free(x) ({ \ |
585 | | typeof(x) __x = (x); \ |
586 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
587 | | .caller = { \ |
588 | | .file = __FILE__, .line = __LINE__, \ |
589 | | .what = MEM_STATS_TYPE_FREE, \ |
590 | | .func = __func__, \ |
591 | | }, \ |
592 | | }; \ |
593 | | HA_WEAK(__start_mem_stats); \ |
594 | | HA_WEAK(__stop_mem_stats); \ |
595 | | if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) { \ |
596 | | HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \ |
597 | | } \ |
598 | | if (*__x) \ |
599 | | _HA_ATOMIC_INC(&_.calls); \ |
600 | | free(*__x); \ |
601 | | *__x = NULL; \ |
602 | | }) |
603 | | |
604 | | #undef malloc |
605 | | #define malloc(x) ({ \ |
606 | | size_t __x = (x); \ |
607 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
608 | | .caller = { \ |
609 | | .file = __FILE__, .line = __LINE__, \ |
610 | | .what = MEM_STATS_TYPE_MALLOC, \ |
611 | | .func = __func__, \ |
612 | | }, \ |
613 | | }; \ |
614 | | HA_WEAK(__start_mem_stats); \ |
615 | | HA_WEAK(__stop_mem_stats); \ |
616 | | _HA_ATOMIC_INC(&_.calls); \ |
617 | | _HA_ATOMIC_ADD(&_.size, __x); \ |
618 | | malloc(__x); \ |
619 | | }) |
620 | | |
621 | | #undef realloc |
622 | | #define realloc(x,y) ({ \ |
623 | | void *__x = (x); size_t __y = (y); \ |
624 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
625 | | .caller = { \ |
626 | | .file = __FILE__, .line = __LINE__, \ |
627 | | .what = MEM_STATS_TYPE_REALLOC, \ |
628 | | .func = __func__, \ |
629 | | }, \ |
630 | | }; \ |
631 | | HA_WEAK(__start_mem_stats); \ |
632 | | HA_WEAK(__stop_mem_stats); \ |
633 | | _HA_ATOMIC_INC(&_.calls); \ |
634 | | _HA_ATOMIC_ADD(&_.size, __y); \ |
635 | | realloc(__x,__y); \ |
636 | | }) |
637 | | |
638 | | #undef strdup |
639 | | #define strdup(x) ({ \ |
640 | | const char *__x = (x); size_t __y = strlen(__x); \ |
641 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
642 | | .caller = { \ |
643 | | .file = __FILE__, .line = __LINE__, \ |
644 | | .what = MEM_STATS_TYPE_STRDUP, \ |
645 | | .func = __func__, \ |
646 | | }, \ |
647 | | }; \ |
648 | | HA_WEAK(__start_mem_stats); \ |
649 | | HA_WEAK(__stop_mem_stats); \ |
650 | | _HA_ATOMIC_INC(&_.calls); \ |
651 | | _HA_ATOMIC_ADD(&_.size, __y); \ |
652 | | strdup(__x); \ |
653 | | }) |
654 | | |
655 | | #undef ha_aligned_alloc |
656 | | #define ha_aligned_alloc(a,s) ({ \ |
657 | | size_t __a = (a); \ |
658 | | size_t __s = (s); \ |
659 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
660 | | .caller = { \ |
661 | | .file = __FILE__, .line = __LINE__, \ |
662 | | .what = MEM_STATS_TYPE_MALLOC, \ |
663 | | .func = __func__, \ |
664 | | }, \ |
665 | | }; \ |
666 | | HA_WEAK(__start_mem_stats); \ |
667 | | HA_WEAK(__stop_mem_stats); \ |
668 | | _HA_ATOMIC_INC(&_.calls); \ |
669 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
670 | | _ha_aligned_alloc(__a, __s); \ |
671 | | }) |
672 | | |
673 | | #undef ha_aligned_zalloc |
674 | | #define ha_aligned_zalloc(a,s) ({ \ |
675 | | size_t __a = (a); \ |
676 | | size_t __s = (s); \ |
677 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
678 | | .caller = { \ |
679 | | .file = __FILE__, .line = __LINE__, \ |
680 | | .what = MEM_STATS_TYPE_CALLOC, \ |
681 | | .func = __func__, \ |
682 | | }, \ |
683 | | }; \ |
684 | | HA_WEAK(__start_mem_stats); \ |
685 | | HA_WEAK(__stop_mem_stats); \ |
686 | | _HA_ATOMIC_INC(&_.calls); \ |
687 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
688 | | _ha_aligned_zalloc(__a, __s); \ |
689 | | }) |
690 | | |
691 | | #undef ha_aligned_alloc_safe |
692 | | #define ha_aligned_alloc_safe(a,s) ({ \ |
693 | | size_t __a = (a); \ |
694 | | size_t __s = (s); \ |
695 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
696 | | .caller = { \ |
697 | | .file = __FILE__, .line = __LINE__, \ |
698 | | .what = MEM_STATS_TYPE_MALLOC, \ |
699 | | .func = __func__, \ |
700 | | }, \ |
701 | | }; \ |
702 | | HA_WEAK(__start_mem_stats); \ |
703 | | HA_WEAK(__stop_mem_stats); \ |
704 | | _HA_ATOMIC_INC(&_.calls); \ |
705 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
706 | | _ha_aligned_alloc_safe(__a, __s); \ |
707 | | }) |
708 | | |
709 | | #undef ha_aligned_zalloc_safe |
710 | | #define ha_aligned_zalloc_safe(a,s) ({ \ |
711 | | size_t __a = (a); \ |
712 | | size_t __s = (s); \ |
713 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
714 | | .caller = { \ |
715 | | .file = __FILE__, .line = __LINE__, \ |
716 | | .what = MEM_STATS_TYPE_CALLOC, \ |
717 | | .func = __func__, \ |
718 | | }, \ |
719 | | }; \ |
720 | | HA_WEAK(__start_mem_stats); \ |
721 | | HA_WEAK(__stop_mem_stats); \ |
722 | | _HA_ATOMIC_INC(&_.calls); \ |
723 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
724 | | _ha_aligned_zalloc_safe(__a, __s); \ |
725 | | }) |
726 | | |
727 | | // Since the type is known, the .extra field will contain its name |
728 | | #undef ha_aligned_alloc_typed |
729 | | #define ha_aligned_alloc_typed(cnt,type) ({ \ |
730 | | size_t __a = __alignof__(type); \ |
731 | | size_t __s = ((size_t)cnt) * sizeof(type); \ |
732 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
733 | | .caller = { \ |
734 | | .file = __FILE__, .line = __LINE__, \ |
735 | | .what = MEM_STATS_TYPE_MALLOC, \ |
736 | | .func = __func__, \ |
737 | | }, \ |
738 | | .extra = #type, \ |
739 | | }; \ |
740 | | HA_WEAK(__start_mem_stats); \ |
741 | | HA_WEAK(__stop_mem_stats); \ |
742 | | _HA_ATOMIC_INC(&_.calls); \ |
743 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
744 | | (type*)_ha_aligned_alloc(__a, __s); \ |
745 | | }) |
746 | | |
747 | | // Since the type is known, the .extra field will contain its name |
748 | | #undef ha_aligned_zalloc_typed |
749 | | #define ha_aligned_zalloc_typed(cnt,type) ({ \ |
750 | | size_t __a = __alignof__(type); \ |
751 | | size_t __s = ((size_t)cnt) * sizeof(type); \ |
752 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
753 | | .caller = { \ |
754 | | .file = __FILE__, .line = __LINE__, \ |
755 | | .what = MEM_STATS_TYPE_CALLOC, \ |
756 | | .func = __func__, \ |
757 | | }, \ |
758 | | .extra = #type, \ |
759 | | }; \ |
760 | | HA_WEAK(__start_mem_stats); \ |
761 | | HA_WEAK(__stop_mem_stats); \ |
762 | | _HA_ATOMIC_INC(&_.calls); \ |
763 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
764 | | (type*)_ha_aligned_zalloc_safe(__a, __s); \ |
765 | | }) |
766 | | |
767 | | #undef ha_aligned_free |
768 | | #define ha_aligned_free(x) ({ \ |
769 | | typeof(x) __x = (x); \ |
770 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
771 | | .caller = { \ |
772 | | .file = __FILE__, .line = __LINE__, \ |
773 | | .what = MEM_STATS_TYPE_FREE, \ |
774 | | .func = __func__, \ |
775 | | }, \ |
776 | | }; \ |
777 | | HA_WEAK(__start_mem_stats); \ |
778 | | HA_WEAK(__stop_mem_stats); \ |
779 | | if (__builtin_constant_p((x))) { \ |
780 | | HA_LINK_ERROR(call_to_ha_aligned_free_attempts_to_free_a_constant); \ |
781 | | } \ |
782 | | if (__x) \ |
783 | | _HA_ATOMIC_INC(&_.calls); \ |
784 | | _ha_aligned_free(__x); \ |
785 | | }) |
786 | | |
787 | | #undef ha_aligned_free_size |
788 | | #define ha_aligned_free_size(p,s) ({ \ |
789 | | void *__p = (p); size_t __s = (s); \ |
790 | | static struct mem_stats _ __attribute__((used,__section__("mem_stats"),__aligned__(sizeof(void*)))) = { \ |
791 | | .caller = { \ |
792 | | .file = __FILE__, .line = __LINE__, \ |
793 | | .what = MEM_STATS_TYPE_FREE, \ |
794 | | .func = __func__, \ |
795 | | }, \ |
796 | | }; \ |
797 | | HA_WEAK(__start_mem_stats); \ |
798 | | HA_WEAK(__stop_mem_stats); \ |
799 | | if (__builtin_constant_p((p))) { \ |
800 | | HA_LINK_ERROR(call_to_ha_aligned_free_attempts_to_free_a_constant); \ |
801 | | } \ |
802 | | if (__p) { \ |
803 | | _HA_ATOMIC_INC(&_.calls); \ |
804 | | _HA_ATOMIC_ADD(&_.size, __s); \ |
805 | | } \ |
806 | | _ha_aligned_free(__p); \ |
807 | | }) |
808 | | |
809 | | #else // DEBUG_MEM_STATS |
810 | | |
811 | | #define will_free(x, y) do { } while (0) |
812 | 0 | #define ha_aligned_alloc(a,s) _ha_aligned_alloc(a, s) |
813 | 0 | #define ha_aligned_zalloc(a,s) _ha_aligned_zalloc(a, s) |
814 | | #define ha_aligned_alloc_safe(a,s) _ha_aligned_alloc_safe(a, s) |
815 | | #define ha_aligned_zalloc_safe(a,s) _ha_aligned_zalloc_safe(a, s) |
816 | 0 | #define ha_aligned_alloc_typed(cnt,type) ((type*)_ha_aligned_alloc(__alignof__(type), ((size_t)cnt) * sizeof(type))) |
817 | 0 | #define ha_aligned_zalloc_typed(cnt,type) ((type*)_ha_aligned_zalloc(__alignof__(type), ((size_t)cnt) * sizeof(type))) |
818 | 0 | #define ha_aligned_free(p) _ha_aligned_free(p) |
819 | 0 | #define ha_aligned_free_size(p,s) _ha_aligned_free(p) |
820 | | |
821 | | #endif /* DEBUG_MEM_STATS*/ |
822 | | |
823 | | /* Add warnings to users of such functions. These will be reported at link time |
824 | | * indicating what file name and line used them. The goal is to remind their |
825 | | * users that these are extremely unsafe functions that never have a valid |
826 | | * reason for being used. |
827 | | */ |
828 | | #undef strcat |
829 | | __attribute__warning("\n" |
830 | | " * WARNING! strcat() must never be used, because there is no convenient way\n" |
831 | | " * to use it that is safe. Use memcpy() instead!\n") |
832 | | extern char *strcat(char *__restrict dest, const char *__restrict src); |
833 | | |
834 | | #undef strcpy |
835 | | __attribute__warning("\n" |
836 | | " * WARNING! strcpy() must never be used, because there is no convenient way\n" |
837 | | " * to use it that is safe. Use memcpy() or strlcpy2() instead!\n") |
838 | | extern char *strcpy(char *__restrict dest, const char *__restrict src); |
839 | | |
840 | | #undef strncat |
841 | | __attribute__warning("\n" |
842 | | " * WARNING! strncat() must never be used, because there is no convenient way\n" |
843 | | " * to use it that is safe. Use memcpy() instead!\n") |
844 | | extern char *strncat(char *__restrict dest, const char *__restrict src, size_t n); |
845 | | |
846 | | #undef sprintf |
847 | | __attribute__warning("\n" |
848 | | " * WARNING! sprintf() must never be used, because there is no convenient way\n" |
849 | | " * to use it that is safe. Use snprintf() instead!\n") |
850 | | extern int sprintf(char *__restrict dest, const char *__restrict fmt, ...); |
851 | | |
852 | | #if defined(_VA_LIST_DEFINED) || defined(_VA_LIST_DECLARED) || defined(_VA_LIST) |
853 | | #undef vsprintf |
854 | | __attribute__warning("\n" |
855 | | " * WARNING! vsprintf() must never be used, because there is no convenient way\n" |
856 | | " * to use it that is safe. Use vsnprintf() instead!\n") |
857 | | extern int vsprintf(char *__restrict dest, const char *__restrict fmt, va_list ap); |
858 | | #endif |
859 | | |
860 | | #endif /* _HAPROXY_BUG_H */ |
861 | | |
862 | | /* |
863 | | * Local variables: |
864 | | * c-indent-level: 8 |
865 | | * c-basic-offset: 8 |
866 | | * End: |
867 | | */ |