/* * include/haproxy/bug.h * Assertions and instant crash macros needed everywhere. * * Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _HAPROXY_BUG_H #define _HAPROXY_BUG_H #include #include /* quick debugging hack, should really be removed ASAP */ #ifdef DEBUG_FULL #define DPRINTF(x...) fprintf(x) #else #define DPRINTF(x...) #endif #define DUMP_TRACE() do { extern void ha_backtrace_to_stderr(void); ha_backtrace_to_stderr(); } while (0) #ifdef DEBUG_USE_ABORT /* abort() is better recognized by code analysis tools */ #define ABORT_NOW() do { DUMP_TRACE(); abort(); } while (0) #else /* More efficient than abort() because it does not mangle the * stack and stops at the exact location we need. */ #define ABORT_NOW() do { DUMP_TRACE(); (*(volatile int*)1=0); my_unreachable(); } while (0) #endif /* This is the generic low-level macro dealing with conditional warnings and * bugs. The caller decides whether to crash or not and what prefix and suffix * to pass. */ #define _BUG_ON(cond, file, line, crash, pfx, sfx) \ __BUG_ON(cond, file, line, crash, pfx, sfx) #define __BUG_ON(cond, file, line, crash, pfx, sfx) \ do { \ if (unlikely(cond)) { \ const char msg[] = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \ DISGUISE(write(2, msg, __builtin_strlen(msg))); \ if (crash) \ ABORT_NOW(); \ else \ DUMP_TRACE(); \ } \ } while (0) /* BUG_ON: complains if is true when DEBUG_STRICT or DEBUG_STRICT_NOCRASH * are set, does nothing otherwise. With DEBUG_STRICT in addition it immediately * crashes using ABORT_NOW() above. */ #if defined(DEBUG_STRICT) #define BUG_ON(cond) _BUG_ON(cond, __FILE__, __LINE__, 1, "FATAL: bug ", "") #elif defined(DEBUG_STRICT_NOCRASH) #define BUG_ON(cond) _BUG_ON(cond, __FILE__, __LINE__, 0, "FATAL: bug ", " (not crashing but process is untrusted now)") #else #define BUG_ON(cond) #endif /* When not optimizing, clang won't remove that code, so only compile it in when optimizing */ #if defined(__GNUC__) && defined(__OPTIMIZE__) #define HA_LINK_ERROR(what) \ do { \ /* provoke a build-time error */ \ extern volatile int what; \ what = 1; \ } while (0) #else #define HA_LINK_ERROR(what) \ do { \ } while (0) #endif /* __OPTIMIZE__ */ /* more reliable free() that clears the pointer */ #define ha_free(x) do { \ typeof(x) __x = (x); \ if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) { \ HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \ } \ free(*__x); \ *__x = NULL; \ } while (0) /* handle 'tainted' status */ enum tainted_flags { TAINTED_CONFIG_EXP_KW_DECLARED = 0x00000001, TAINTED_ACTION_EXP_EXECUTED = 0x00000002, TAINTED_CLI_EXPERT_MODE = 0x00000004, TAINTED_CLI_EXPERIMENTAL_MODE = 0x00000008, }; /* this is a bit field made of TAINTED_*, and is declared in haproxy.c */ extern unsigned int tainted; static inline void mark_tainted(const enum tainted_flags flag) { HA_ATOMIC_OR(&tainted, flag); } static inline unsigned int get_tainted() { return HA_ATOMIC_LOAD(&tainted); } #if defined(DEBUG_MEM_STATS) #include #include /* Memory allocation statistics are centralized into a global "mem_stats" * section. This will not work with some linkers. */ enum { MEM_STATS_TYPE_UNSET = 0, MEM_STATS_TYPE_CALLOC, MEM_STATS_TYPE_FREE, MEM_STATS_TYPE_MALLOC, MEM_STATS_TYPE_REALLOC, MEM_STATS_TYPE_STRDUP, }; struct mem_stats { size_t calls; size_t size; const char *file; int line; int type; }; #undef calloc #define calloc(x,y) ({ \ size_t __x = (x); size_t __y = (y); \ static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ .file = __FILE__, .line = __LINE__, \ .type = MEM_STATS_TYPE_CALLOC, \ }; \ __asm__(".globl __start_mem_stats"); \ __asm__(".globl __stop_mem_stats"); \ _HA_ATOMIC_INC(&_.calls); \ _HA_ATOMIC_ADD(&_.size, __x * __y); \ calloc(__x,__y); \ }) /* note: we can't redefine free() because we have a few variables and struct * members called like this. */ #undef __free #define __free(x) ({ \ void *__x = (x); \ static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ .file = __FILE__, .line = __LINE__, \ .type = MEM_STATS_TYPE_FREE, \ }; \ __asm__(".globl __start_mem_stats"); \ __asm__(".globl __stop_mem_stats"); \ if (__x) \ _HA_ATOMIC_INC(&_.calls); \ free(__x); \ }) #undef ha_free #define ha_free(x) ({ \ typeof(x) __x = (x); \ static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ .file = __FILE__, .line = __LINE__, \ .type = MEM_STATS_TYPE_FREE, \ }; \ __asm__(".globl __start_mem_stats"); \ __asm__(".globl __stop_mem_stats"); \ if (__builtin_constant_p((x)) || __builtin_constant_p(*(x))) { \ HA_LINK_ERROR(call_to_ha_free_attempts_to_free_a_constant); \ } \ if (*__x) \ _HA_ATOMIC_INC(&_.calls); \ free(*__x); \ *__x = NULL; \ }) #undef malloc #define malloc(x) ({ \ size_t __x = (x); \ static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ .file = __FILE__, .line = __LINE__, \ .type = MEM_STATS_TYPE_MALLOC, \ }; \ __asm__(".globl __start_mem_stats"); \ __asm__(".globl __stop_mem_stats"); \ _HA_ATOMIC_INC(&_.calls); \ _HA_ATOMIC_ADD(&_.size, __x); \ malloc(__x); \ }) #undef realloc #define realloc(x,y) ({ \ void *__x = (x); size_t __y = (y); \ static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ .file = __FILE__, .line = __LINE__, \ .type = MEM_STATS_TYPE_REALLOC, \ }; \ __asm__(".globl __start_mem_stats"); \ __asm__(".globl __stop_mem_stats"); \ _HA_ATOMIC_INC(&_.calls); \ _HA_ATOMIC_ADD(&_.size, __y); \ realloc(__x,__y); \ }) #undef strdup #define strdup(x) ({ \ const char *__x = (x); size_t __y = strlen(__x); \ static struct mem_stats _ __attribute__((used,__section__("mem_stats"))) = { \ .file = __FILE__, .line = __LINE__, \ .type = MEM_STATS_TYPE_STRDUP, \ }; \ __asm__(".globl __start_mem_stats"); \ __asm__(".globl __stop_mem_stats"); \ _HA_ATOMIC_INC(&_.calls); \ _HA_ATOMIC_ADD(&_.size, __y); \ strdup(__x); \ }) #endif /* DEBUG_MEM_STATS*/ #endif /* _HAPROXY_BUG_H */ /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * End: */