24#include <freeradius-devel/util/backtrace.h>
25#include <freeradius-devel/util/debug.h>
26#include <freeradius-devel/util/hash.h>
27#include <freeradius-devel/util/strerror.h>
28#include <freeradius-devel/util/syserror.h>
36#if defined(HAVE_MALLOPT) && defined(HAVE_MALLOC_H)
40#ifdef HAVE_SYS_PRCTL_H
41# include <sys/prctl.h>
44#ifdef HAVE_SYS_PROCCTL_H
45# include <sys/procctl.h>
48#ifdef HAVE_SYS_PTRACE_H
49# include <sys/ptrace.h>
50# if !defined(PT_ATTACH) && defined(PTRACE_ATTACH)
51# define PT_ATTACH PTRACE_ATTACH
53# if !defined(PT_DETACH) && defined(PTRACE_DETACH)
54# define PT_DETACH PTRACE_DETACH
58#ifdef HAVE_SYS_RESOURCE_H
59# include <sys/resource.h>
63#include <sys/sysctl.h>
76#ifdef HAVE_SYS_RESOURCE_H
77static struct rlimit init_core_limit;
86#if defined(HAVE_SYS_PTRACE_H)
88# define _PTRACE(_x, _y) ptrace(_x, _y, NULL, NULL)
89# define _PTRACE_DETACH(_x) ptrace(PT_DETACH, _x, NULL, NULL)
90# elif !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(HAVE_SYS_PROCCTL_H)
91# define _PTRACE(_x, _y) ptrace(_x, _y, NULL, 0)
92# define _PTRACE_DETACH(_x) ptrace(PT_DETACH, _x, (void *)1, 0)
95# ifdef HAVE_CAPABILITY_H
96# include <sys/capability.h>
100#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
101# include <sanitizer/lsan_interface.h>
104#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
105static int lsan_test_pipe[2] = {-1, -1};
106static int lsan_test_pid = -1;
107static int lsan_state = INT_MAX;
108static bool lsan_disable =
false;
124 "leak:CRYPTO_THREAD_lock_new\n"
127 "leak:rd_kafka_sasl_cyrus_global_init\n"
128#if defined(__APPLE__)
130 "leak:ImageLoaderMachO::doImageInit\n"
131 "leak:initializeNonMetaClass\n"
132 "leak:_st_tzset_basic\n"
133 "leak:attachCategories\n"
137 "leak:libSystem_atfork_child\n"
138 "leak:libsystem_notify\n"
142 "leak:perl_construct\n"
143 "leak:realizeClassWithoutSwift\n"
145 "leak:tzsetwall_basic\n"
152 "leak:libobjc.A.dylib\n"
153 "leak:CoreFoundation.framework\n"
154 "leak:td::__1::thread::thread\n"
155#elif defined(__linux__)
156 "leak:*getpwnam_r*\n"
162 "leak:libdigestmd5\n"
172char const CC_HINT(
used) *__lsan_default_options(
void)
174 return "print_suppressions=0";
180int CC_HINT(
used) __lsan_is_turned_off(
void)
185 if (lsan_disable)
return 1;
188 if (lsan_test_pid != 0)
return 0;
191 if (write(lsan_test_pipe[1], &ret,
sizeof(ret)) < 0) {
192 fprintf(stderr,
"Writing LSAN status failed: %s",
fr_syserror(errno));
194 close(lsan_test_pipe[1]);
211 if (lsan_state != INT_MAX)
return lsan_state;
213 if (pipe(lsan_test_pipe) < 0) {
218 lsan_test_pid = fork();
219 if (lsan_test_pid == -1) {
225 if (lsan_test_pid == 0) {
226 close(lsan_test_pipe[0]);
231 close(lsan_test_pipe[1]);
233 while ((read(lsan_test_pipe[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
235 close(lsan_test_pipe[0]);
238 waitpid(lsan_test_pid, NULL, 0);
252#if defined(HAVE_SYS_PROCCTL_H)
257 if (procctl(P_PID, getpid(), PROC_TRACE_STATUS, &status) == -1) {
275#elif defined(__APPLE__)
290 struct kinfo_proc info;
297 info.kp_proc.p_flag = 0;
305 mib[2] = KERN_PROC_PID;
311 if (ret != 0)
return -1;
314 return ((info.kp_proc.p_flag & P_TRACED) != 0);
316#elif defined(HAVE_SYS_PTRACE_H) && !defined(__EMSCRIPTEN__)
329 int from_child[2] = {-1, -1};
331#ifdef HAVE_CAPABILITY_H
332 cap_flag_value_t state;
340 caps = cap_get_proc();
346 if (cap_get_flag(caps, CAP_SYS_PTRACE,
CAP_PERMITTED, &state) < 0) {
353 if ((state == CAP_SET) && (cap_get_flag(caps, CAP_SYS_PTRACE,
CAP_EFFECTIVE, &state) < 0)) {
363 if (state == CAP_CLEAR) {
364 fr_strerror_printf(
"ptrace capability not set. If debugger detection is required run as root or: "
365 "setcap cap_sys_ptrace+ep <path_to_binary>");
372 if (pipe(from_child) < 0) {
386 int ppid = getppid();
393#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
399DIAG_ON(deprecated-declarations)
402 close(from_child[0]);
413 _PTRACE(flags, ppid);
416 waitpid(ppid, NULL, 0);
420 if (write(from_child[1], &ret,
sizeof(ret)) < 0) {
421 fprintf(stderr,
"Writing ptrace status to parent failed: %s\n",
fr_syserror(errno));
425 _PTRACE_DETACH(ppid);
458 }
else if (errno == EPERM) {
468 fprintf(stderr,
"Debugger check failed to attach to parent with unexpected error: %s\n",
fr_syserror(errno));
480 while ((read(from_child[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
483 close(from_child[1]);
484 close(from_child[0]);
487 waitpid(pid, NULL, 0);
529 return "Debug state unknown (ptrace functionality not available)";
532 return "Debug state unknown (cap_sys_ptrace capability not set)";
535 return "Debug state unknown";
538 return "Found debugger attached";
541 return "Debugger not attached";
556 if (always) raise(SIGTRAP);
560 fprintf(stderr,
"Debugger detected, raising SIGTRAP\n");
582 ptr = talloc(ctx,
char);
590#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) && !defined(__EMSCRIPTEN__)
593 if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
601#elif defined(HAVE_SYS_PROCCTL_H)
604 int mode = dumpable ? PROC_TRACE_CTL_ENABLE : PROC_TRACE_CTL_DISABLE;
606 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode) == -1) {
625#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE) && !defined(__EMSCRIPTEN__)
630 ret = prctl(PR_GET_DUMPABLE);
639 if (ret != 1)
return 0;
642#elif defined(HAVE_SYS_PROCCTL_H)
647 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &status) == -1) {
656 if (status != PROC_TRACE_CTL_ENABLE)
return 0;
675#ifdef HAVE_SYS_RESOURCE_H
676 if (getrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
692#ifdef HAVE_SYS_RESOURCE_H
694 struct rlimit current;
699 if (getrlimit(RLIMIT_CORE, ¤t) < 0) {
704 if (allow_core_dumps) {
705 if ((current.rlim_cur != init_core_limit.rlim_cur) ||
706 (current.rlim_max != init_core_limit.rlim_max)) {
707 if (setrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
724 }
else if (current.rlim_cur != 0) {
725 struct rlimit no_core;
727 no_core.rlim_cur = 0;
728 no_core.rlim_max = current.rlim_max;
730 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
741#if defined(HAVE_SYS_PROCCTL_H) || (defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE))
788 fr_strerror_const(
"Failed writing panic_action to temporary buffer (truncated)");
796 if (stat(p, &statbuf) == 0) {
798 if ((statbuf.st_mode & S_IWOTH) != 0) {
816 size_t left =
sizeof(cmd), ret;
837 memset(cmd, 0,
sizeof(cmd));
863 while ((q = strstr(p,
"%p"))) {
864 out += ret =
snprintf(
out, left,
"%.*s%d", (
int) (q - p), p, (
int) getpid());
873 if (strlen(p) >= left)
goto oob;
877 bool disable =
false;
977#define TALLOC_REPORT_MAX_DEPTH 20
987 log = fdopen(fd,
"w");
995 fprintf(log,
"Current state of talloced memory:\n");
1000 fprintf(log,
"Talloc chunk lineage:\n");
1001 fprintf(log,
"%p (%s)", ctx, talloc_get_name(ctx));
1005 fprintf(log,
" < %p (%s)", ctx, talloc_get_name(ctx));
1012 fprintf(log,
"Talloc context level %i:\n", i++);
1013 talloc_report_full(ctx, log);
1014 }
while ((ctx = talloc_parent(ctx)) &&
1027 talloc_disable_null_tracking();
1041 marker = talloc(ctx,
bool);
1071 static bool setup =
false;
1076 char const *p = cmd;
1083 while ((q = strstr(p,
"%e"))) {
1093 if (strlen(p) >= left)
goto oob;
1113 env = getenv(
"DEBUGGER_ATTACHED");
1114 if (env && (strcmp(env,
"yes") == 0)) {
1117 }
else if (env && (strcmp(env,
"no") == 0)) {
1173#if defined(HAVE_MALLOPT) && !defined(NDEBUG)
1180 if (!getenv(
"TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
1182# ifdef M_CHECK_ACTION
1183 mallopt(M_CHECK_ACTION, 3);
1229 char buffer[(0x10 * 3) + 1];
1232 for (i = 0; i < data_len; i += 0x10) {
1234 if ((i + len) > data_len) len = data_len - i;
1236 for (p =
buffer, j = 0; j < len; j++, p += 3)
snprintf(p, end - p,
"%02x ",
data[i + j]);
1330 if (status != EXIT_SUCCESS) {
1333 if (error && *error && (status != 0)) {
1334 FR_FAULT_LOG(
"%sEXIT(%i) CALLED %s[%d]. Last error was: %s", now ?
"_" :
"",
1343 if (now) _Exit(status);
1351 if (now) _Exit(status);
static int const char char buffer[256]
void fr_backtrace_init(UNUSED char const *program)
#define NEVER_RETURNS
Should be placed before the function return type.
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.
char const * fr_debug_state_to_msg(fr_debug_state_t state)
Return current value of debug_state.
static int _disable_null_tracking(UNUSED bool *p)
static int _panic_on_free(UNUSED char *foo)
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.
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.
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.
#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.
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.
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.
int(* fr_fault_cb_t)(int signum)
Optional callback passed to fr_fault_setup.
#define FR_FAULT_LOG(_fmt,...)
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
char const * __lsan_default_suppressions(void)
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 char const * program
PUBLIC int vsnprintf(char *string, size_t length, char *format, va_list args)
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.
TALLOC_CTX * talloc_autofree_context_global(void)
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