24 #include <freeradius-devel/util/debug.h>
25 #include <freeradius-devel/util/hash.h>
26 #include <freeradius-devel/util/strerror.h>
27 #include <freeradius-devel/util/syserror.h>
35 #if defined(HAVE_MALLOPT) && defined(HAVE_MALLOC_H)
44 # include <execinfo.h>
47 #ifdef HAVE_SYS_PRCTL_H
48 # include <sys/prctl.h>
51 #ifdef HAVE_SYS_PROCCTL_H
52 # include <sys/procctl.h>
55 #ifdef HAVE_SYS_PTRACE_H
56 # include <sys/ptrace.h>
57 # if !defined(PT_ATTACH) && defined(PTRACE_ATTACH)
58 # define PT_ATTACH PTRACE_ATTACH
60 # if !defined(PT_DETACH) && defined(PTRACE_DETACH)
61 # define PT_DETACH PTRACE_DETACH
65 #ifdef HAVE_SYS_RESOURCE_H
66 # include <sys/resource.h>
70 #include <sys/sysctl.h>
74 # ifndef MAX_BT_FRAMES
75 # define MAX_BT_FRAMES 128
78 # define MAX_BT_CBUFF 1048576
85 void *frames[MAX_BT_FRAMES];
106 #ifdef HAVE_SYS_RESOURCE_H
107 static struct rlimit init_core_limit;
118 #if defined(HAVE_SYS_PTRACE_H)
120 # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, NULL)
121 # define _PTRACE_DETACH(_x) ptrace(PT_DETACH, _x, NULL, NULL)
122 # elif !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(HAVE_SYS_PROCCTL_H)
123 # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, 0)
124 # define _PTRACE_DETACH(_x) ptrace(PT_DETACH, _x, (void *)1, 0)
127 # ifdef HAVE_CAPABILITY_H
128 # include <sys/capability.h>
132 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
133 # include <sanitizer/lsan_interface.h>
136 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
137 static int lsan_test_pipe[2] = {-1, -1};
138 static int lsan_test_pid = -1;
139 static int lsan_state = INT_MAX;
140 static bool lsan_disable =
false;
153 char const CC_HINT(
used) *__lsan_default_suppressions(
void)
156 "leak:CRYPTO_THREAD_lock_new\n"
157 #if defined(__APPLE__)
159 "leak:ImageLoaderMachO::doImageInit\n"
160 "leak:_st_tzset_basic\n"
161 "leak:attachCategories\n"
165 "leak:libSystem_atfork_child\n"
166 "leak:libsystem_notify\n"
170 "leak:perl_construct\n"
171 "leak:realizeClassWithoutSwift\n"
173 "leak:tzsetwall_basic\n"
174 #elif defined(__linux__)
175 "leak:*getpwnam_r*\n"
188 char const CC_HINT(
used) *__lsan_default_options(
void)
190 return "print_suppressions=0";
196 int CC_HINT(
used) __lsan_is_turned_off(
void)
201 if (lsan_disable)
return 1;
204 if (lsan_test_pid != 0)
return 0;
207 if (write(lsan_test_pipe[1], &ret,
sizeof(ret)) < 0) {
208 fprintf(stderr,
"Writing LSAN status failed: %s",
fr_syserror(errno));
210 close(lsan_test_pipe[1]);
227 if (lsan_state != INT_MAX)
return lsan_state;
229 if (pipe(lsan_test_pipe) < 0) {
234 lsan_test_pid = fork();
235 if (lsan_test_pid == -1) {
241 if (lsan_test_pid == 0) {
242 close(lsan_test_pipe[0]);
247 close(lsan_test_pipe[1]);
249 while ((read(lsan_test_pipe[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
251 close(lsan_test_pipe[0]);
254 waitpid(lsan_test_pid, NULL, 0);
268 #if defined(HAVE_SYS_PROCCTL_H)
273 if (procctl(P_PID, getpid(), PROC_TRACE_STATUS, &status) == -1) {
291 #elif defined(__APPLE__)
306 struct kinfo_proc info;
313 info.kp_proc.p_flag = 0;
321 mib[2] = KERN_PROC_PID;
326 ret = sysctl(mib,
NUM_ELEMENTS(mib), &info, &size, NULL, 0);
327 if (ret != 0)
return -1;
330 return ((info.kp_proc.p_flag & P_TRACED) != 0);
332 #elif defined(HAVE_SYS_PTRACE_H) && !defined(__EMSCRIPTEN__)
345 int from_child[2] = {-1, -1};
347 #ifdef HAVE_CAPABILITY_H
348 cap_flag_value_t state;
356 caps = cap_get_proc();
362 if (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_PERMITTED, &state) < 0) {
369 if ((state == CAP_SET) && (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &state) < 0)) {
379 if (state == CAP_CLEAR) {
380 fr_strerror_printf(
"ptrace capability not set. If debugger detection is required run as root or: "
381 "setcap cap_sys_ptrace+ep <path_to_binary>");
388 if (pipe(from_child) < 0) {
402 int ppid = getppid();
409 #ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
415 DIAG_ON(deprecated-declarations)
418 close(from_child[0]);
429 _PTRACE(flags, ppid);
436 if (write(from_child[1], &ret,
sizeof(ret)) < 0) {
437 fprintf(stderr,
"Writing ptrace status to parent failed: %s\n",
fr_syserror(errno));
441 _PTRACE_DETACH(ppid);
474 }
else if (errno == EPERM) {
484 fprintf(stderr,
"Debugger check failed to attach to parent with unexpected error: %s\n",
fr_syserror(errno));
496 while ((read(from_child[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
499 close(from_child[1]);
500 close(from_child[0]);
545 return "Debug state unknown (ptrace functionality not available)";
548 return "Debug state unknown (cap_sys_ptrace capability not set)";
551 return "Debug state unknown";
554 return "Found debugger attached";
557 return "Debugger not attached";
572 if (always)
raise(SIGTRAP);
576 fprintf(stderr,
"Debugger detected, raising SIGTRAP\n");
595 if ((p->obj == obj) || !obj) {
598 fprintf(stderr,
"Stacktrace for: %p\n", p->obj);
599 backtrace_symbols_fd(p->frames, p->count, STDERR_FILENO);
604 fprintf(stderr,
"No backtrace available for %p", obj);
618 bt = talloc_zero(NULL, fr_bt_info_t);
621 bt->obj = marker->obj;
622 bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
665 if (*fring == NULL) {
666 pthread_mutex_lock(&fr_debug_init);
668 if (*fring == NULL) *fring =
fr_fring_alloc(NULL, MAX_BT_CBUFF,
true);
669 pthread_mutex_unlock(&fr_debug_init);
677 marker->obj = (
void *) obj;
678 marker->fring = *fring;
680 fprintf(stderr,
"Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
692 fprintf(stderr,
"Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
696 fprintf(stderr,
"Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
716 ptr = talloc(ctx,
char);
724 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) && !defined(__EMSCRIPTEN__)
727 if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
735 #elif defined(HAVE_SYS_PROCCTL_H)
738 int mode = dumpable ? PROC_TRACE_CTL_ENABLE : PROC_TRACE_CTL_DISABLE;
740 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode) == -1) {
759 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE) && !defined(__EMSCRIPTEN__)
764 ret = prctl(PR_GET_DUMPABLE);
773 if (ret != 1)
return 0;
776 #elif defined(HAVE_SYS_PROCCTL_H)
781 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &status) == -1) {
790 if (status != PROC_TRACE_CTL_ENABLE)
return 0;
809 #ifdef HAVE_SYS_RESOURCE_H
810 if (getrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
826 #ifdef HAVE_SYS_RESOURCE_H
833 if (getrlimit(RLIMIT_CORE, &
current) < 0) {
838 if (allow_core_dumps) {
839 if ((
current.rlim_cur != init_core_limit.rlim_cur) ||
840 (
current.rlim_max != init_core_limit.rlim_max)) {
841 if (setrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
858 }
else if (
current.rlim_cur != 0) {
859 struct rlimit no_core;
861 no_core.rlim_cur = 0;
862 no_core.rlim_max =
current.rlim_max;
864 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
875 #if defined(HAVE_SYS_PROCCTL_H) || (defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE))
922 fr_strerror_const(
"Failed writing panic_action to temporary buffer (truncated)");
930 if (stat(p, &statbuf) == 0) {
932 if ((statbuf.st_mode & S_IWOTH) != 0) {
956 #if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
959 void *
stack[MAX_BT_FRAMES];
961 frame_count = backtrace(
stack, MAX_BT_FRAMES);
963 FR_FAULT_LOG(
"Backtrace of last %zu frames:", frame_count);
979 size_t left =
sizeof(cmd), ret;
1000 memset(cmd, 0,
sizeof(cmd));
1026 while ((q = strstr(p,
"%p"))) {
1027 out += ret =
snprintf(
out, left,
"%.*s%d", (
int) (q - p), p, (
int) getpid());
1036 if (strlen(p) >= left)
goto oob;
1040 bool disable =
false;
1104 #if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
1107 void *
stack[MAX_BT_FRAMES];
1109 frame_count = backtrace(
stack, MAX_BT_FRAMES);
1110 FR_FAULT_LOG(
"Backtrace of last %zu frames:", frame_count);
1149 #define TALLOC_REPORT_MAX_DEPTH 20
1159 log = fdopen(fd,
"w");
1167 fprintf(log,
"Current state of talloced memory:\n");
1172 fprintf(log,
"Talloc chunk lineage:\n");
1173 fprintf(log,
"%p (%s)", ctx, talloc_get_name(ctx));
1177 fprintf(log,
" < %p (%s)", ctx, talloc_get_name(ctx));
1184 fprintf(log,
"Talloc context level %i:\n", i++);
1185 talloc_report_full(ctx, log);
1186 }
while ((ctx = talloc_parent(ctx)) &&
1200 talloc_disable_null_tracking();
1214 marker = talloc(ctx,
bool);
1244 static bool setup =
false;
1249 char const *p = cmd;
1256 while ((q = strstr(p,
"%e"))) {
1257 out += ret =
snprintf(
out, left,
"%.*s%s", (
int) (q - p), p, program ? program :
"");
1266 if (strlen(p) >= left)
goto oob;
1286 env = getenv(
"DEBUGGER_ATTACHED");
1287 if (env && (strcmp(env,
"yes") == 0)) {
1290 }
else if (env && (strcmp(env,
"no") == 0)) {
1346 #if defined(HAVE_MALLOPT) && !defined(NDEBUG)
1353 if (!getenv(
"TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
1355 # ifdef M_CHECK_ACTION
1356 mallopt(M_CHECK_ACTION, 3);
1360 #if defined(HAVE_EXECINFO) && defined(__GNUC__) && !defined(NDEBUG)
1373 backtrace(
stack, 10);
1418 char buffer[(0x10 * 3) + 1];
1421 for (i = 0; i < data_len; i += 0x10) {
1423 if ((i + len) > data_len) len = data_len - i;
1425 for (p =
buffer, j = 0; j < len; j++, p += 3)
snprintf(p, end - p,
"%02x ",
data[i + j]);
1519 if (status != EXIT_SUCCESS) {
1522 if (error && *error && (status != 0)) {
1523 FR_FAULT_LOG(
"%sEXIT(%i) CALLED %s[%u]. Last error was: %s", now ?
"_" :
"",
1532 if (now) _Exit(status);
1540 if (now) _Exit(status);
1556 if ((size + 4) == offset) {
vsnprintf(buffer, sizeof(buffer), fmt, args)
static int const char char buffer[256]
#define NEVER_RETURNS
Should be placed before the function return type.
fr_dcursor_iter_t void * current
static int fr_fault_check_permissions(void)
Check to see if panic_action file is world writable.
static char panic_action[512]
The command to execute when panicking.
fr_bt_marker_t * fr_backtrace_attach(UNUSED fr_fring_t **fring, UNUSED TALLOC_CTX *obj)
static int _disable_null_tracking(UNUSED bool *p)
static int _panic_on_free(UNUSED char *foo)
char const * fr_debug_state_to_msg(fr_debug_state_t state)
Return current value of debug_state.
void fr_disable_null_tracking_on_free(TALLOC_CTX *ctx)
Disable the null tracking context when a talloc chunk is freed.
bool _fr_assert_fail(char const *file, int line, char const *expr, char const *msg,...)
A soft assertion which triggers the fault handler in debug builds.
void fr_fault_set_log_fd(int fd)
Set a file descriptor to log memory reports to.
static bool dump_core
Whether we should drop a core on fatal signals.
int fr_log_talloc_report(TALLOC_CTX const *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
int fr_set_dumpable(bool allow_core_dumps)
Enable or disable core dumps.
static void _fr_talloc_fault_simple(char const *reason)
Callback executed on fatal talloc error.
void fr_verify_struct(void const *ptr, size_t size, size_t offset)
static int fr_set_pr_dumpable_flag(UNUSED bool dumpable)
Set the dumpable flag, also controls whether processes can PATTACH.
static fr_fault_cb_t panic_cb
Callback to execute whilst panicking, before the panic_action.
static TALLOC_CTX * talloc_autofree_ctx
void fr_fault_backtrace(void)
Split out so it can be sprinkled throughout the server and called via a debugger.
static uint32_t fr_hash_struct(void const *ptr, size_t size, size_t offset)
static int fr_fault_log_fd
Where to write debug output.
int fr_reset_dumpable(void)
Reset dumpable state to previously configured value.
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
void fr_verify_struct_member(void const *ptr, size_t len, uint32_t *signature)
#define TALLOC_REPORT_MAX_DEPTH
NEVER_RETURNS void fr_fault(int sig)
Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
void fr_sign_struct(void *ptr, size_t size, size_t offset)
int fr_get_lsan_state(void)
void fr_debug_state_store(void)
Should be run before using setuid or setgid to get useful results.
NEVER_RETURNS void _fr_exit(char const *file, int line, int status, bool now)
Exit possibly printing a message about why we're exiting.
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
void fr_debug_break(bool always)
Break in debugger (if were running under a debugger)
void fr_fault_set_cb(fr_fault_cb_t func)
Set a callback to be called before fr_fault()
void _fr_assert_fatal(char const *file, int line, char const *expr, char const *msg,...)
A fatal assertion which triggers the fault handler in debug builds or exits.
void fr_fault_log_hex(uint8_t const *data, size_t data_len)
Print data as a hex block.
int fr_set_dumpable_init(void)
Get the current maximum for core files.
void fr_panic_on_free(TALLOC_CTX *ctx)
Insert memory into the context of another talloc memory chunk which causes a panic when freed.
static void _fr_talloc_fault(char const *reason)
Callback executed on fatal talloc error.
static void _fr_talloc_log(char const *msg)
Wrapper to pass talloc log output to our fr_fault_log function.
static int fr_get_pr_dumpable_flag(void)
Get the processes dumpable flag.
fr_debug_state_t fr_debug_state
Whether we're attached to by a debugger.
void backtrace_print(UNUSED fr_fring_t *fring, UNUSED void *obj)
int fr_get_debug_state(void)
void fr_fault_log(char const *msg,...)
Log output to the fr_fault_log_fd.
@ DEBUGGER_STATE_NOT_ATTACHED
We can attach, so a debugger must not be.
@ DEBUGGER_STATE_UNKNOWN_NO_PTRACE
We don't have ptrace so can't check.
@ DEBUGGER_STATE_UNKNOWN_NO_PTRACE_CAP
CAP_SYS_PTRACE not set for the process.
@ DEBUGGER_STATE_UNKNOWN
Unknown, likely fr_get_debug_state() not called yet.
@ DEBUGGER_STATE_ATTACHED
We can't attach, it's likely a debugger is already tracing.
struct fr_bt_marker fr_bt_marker_t
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
int(* fr_fault_cb_t)(int signum)
Optional callback passed to fr_fault_setup.
#define FR_FAULT_LOG(_fmt,...)
int fr_backtrace_do(fr_bt_marker_t *marker)
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
void * fr_fring_next(fr_fring_t *fring)
Remove an item from the buffer.
fr_fring_t * fr_fring_alloc(TALLOC_CTX *ctx, uint32_t size, bool lock)
Initialise a ring buffer with fixed element size.
int fr_fring_overwrite(fr_fring_t *fring, void *in)
Insert a new item into the circular buffer, freeing the tail if we hit it.
Standard thread safe circular buffer.
uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
uint32_t fr_hash(void const *data, size_t size)
waitpid(reap->pid_ev->pid, &status, 0)
static char * stack[MAX_STACK]
int fr_unset_signal(int sig)
Uninstall a signal for a specific handler.
int fr_set_signal(int sig, sig_t func)
Sets a signal handler using sigaction if available, else signal.
int vdprintf(int fd, char const *format, va_list args)
#define is_truncated(_ret, _max)
static unsigned int hash(char const *username, unsigned int tablesize)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
size_t strlcpy(char *dst, char const *src, size_t siz)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
void * talloc_null_ctx(void)
Retrieve the current talloc NULL ctx.
char const * fr_strerror(void)
Get the last library error.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const(_msg)
static size_t char ** out