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;
88#if defined(HAVE_SYS_PTRACE_H)
90# define _PTRACE(_x, _y) ptrace(_x, _y, NULL, NULL)
91# define _PTRACE_DETACH(_x) ptrace(PT_DETACH, _x, NULL, NULL)
92# elif !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(HAVE_SYS_PROCCTL_H)
93# define _PTRACE(_x, _y) ptrace(_x, _y, NULL, 0)
94# define _PTRACE_DETACH(_x) ptrace(PT_DETACH, _x, (void *)1, 0)
97# ifdef HAVE_CAPABILITY_H
98# include <sys/capability.h>
102#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
103# include <sanitizer/lsan_interface.h>
106#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
107static int lsan_test_pipe[2] = {-1, -1};
108static int lsan_test_pid = -1;
109static int lsan_state = INT_MAX;
110static bool lsan_disable =
false;
123char const CC_HINT(
used) *__lsan_default_suppressions(
void)
126 "leak:CRYPTO_THREAD_lock_new\n"
129 "leak:rd_kafka_sasl_cyrus_global_init\n"
130#if defined(__APPLE__)
132 "leak:ImageLoaderMachO::doImageInit\n"
133 "leak:initializeNonMetaClass\n"
134 "leak:_st_tzset_basic\n"
135 "leak:attachCategories\n"
139 "leak:libSystem_atfork_child\n"
140 "leak:libsystem_notify\n"
144 "leak:perl_construct\n"
145 "leak:realizeClassWithoutSwift\n"
147 "leak:tzsetwall_basic\n"
154 "leak:libobjc.A.dylib\n"
155 "leak:CoreFoundation.framework\n"
156#elif defined(__linux__)
157 "leak:*getpwnam_r*\n"
170char const CC_HINT(
used) *__lsan_default_options(
void)
172 return "print_suppressions=0";
178int CC_HINT(
used) __lsan_is_turned_off(
void)
183 if (lsan_disable)
return 1;
186 if (lsan_test_pid != 0)
return 0;
189 if (write(lsan_test_pipe[1], &ret,
sizeof(ret)) < 0) {
190 fprintf(stderr,
"Writing LSAN status failed: %s",
fr_syserror(errno));
192 close(lsan_test_pipe[1]);
209 if (lsan_state != INT_MAX)
return lsan_state;
211 if (pipe(lsan_test_pipe) < 0) {
216 lsan_test_pid = fork();
217 if (lsan_test_pid == -1) {
223 if (lsan_test_pid == 0) {
224 close(lsan_test_pipe[0]);
229 close(lsan_test_pipe[1]);
231 while ((read(lsan_test_pipe[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
233 close(lsan_test_pipe[0]);
236 waitpid(lsan_test_pid, NULL, 0);
250#if defined(HAVE_SYS_PROCCTL_H)
255 if (procctl(P_PID, getpid(), PROC_TRACE_STATUS, &status) == -1) {
273#elif defined(__APPLE__)
288 struct kinfo_proc info;
295 info.kp_proc.p_flag = 0;
303 mib[2] = KERN_PROC_PID;
309 if (ret != 0)
return -1;
312 return ((info.kp_proc.p_flag & P_TRACED) != 0);
314#elif defined(HAVE_SYS_PTRACE_H) && !defined(__EMSCRIPTEN__)
327 int from_child[2] = {-1, -1};
329#ifdef HAVE_CAPABILITY_H
330 cap_flag_value_t state;
338 caps = cap_get_proc();
344 if (cap_get_flag(caps, CAP_SYS_PTRACE,
CAP_PERMITTED, &state) < 0) {
351 if ((state == CAP_SET) && (cap_get_flag(caps, CAP_SYS_PTRACE,
CAP_EFFECTIVE, &state) < 0)) {
361 if (state == CAP_CLEAR) {
362 fr_strerror_printf(
"ptrace capability not set. If debugger detection is required run as root or: "
363 "setcap cap_sys_ptrace+ep <path_to_binary>");
370 if (pipe(from_child) < 0) {
384 int ppid = getppid();
391#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
397DIAG_ON(deprecated-declarations)
400 close(from_child[0]);
411 _PTRACE(flags, ppid);
414 waitpid(ppid, NULL, 0);
418 if (write(from_child[1], &ret,
sizeof(ret)) < 0) {
419 fprintf(stderr,
"Writing ptrace status to parent failed: %s\n",
fr_syserror(errno));
423 _PTRACE_DETACH(ppid);
456 }
else if (errno == EPERM) {
466 fprintf(stderr,
"Debugger check failed to attach to parent with unexpected error: %s\n",
fr_syserror(errno));
478 while ((read(from_child[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
481 close(from_child[1]);
482 close(from_child[0]);
485 waitpid(pid, NULL, 0);
527 return "Debug state unknown (ptrace functionality not available)";
530 return "Debug state unknown (cap_sys_ptrace capability not set)";
533 return "Debug state unknown";
536 return "Found debugger attached";
539 return "Debugger not attached";
554 if (always) raise(SIGTRAP);
558 fprintf(stderr,
"Debugger detected, raising SIGTRAP\n");
580 ptr = talloc(ctx,
char);
588#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) && !defined(__EMSCRIPTEN__)
591 if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
599#elif defined(HAVE_SYS_PROCCTL_H)
602 int mode = dumpable ? PROC_TRACE_CTL_ENABLE : PROC_TRACE_CTL_DISABLE;
604 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode) == -1) {
623#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE) && !defined(__EMSCRIPTEN__)
628 ret = prctl(PR_GET_DUMPABLE);
637 if (ret != 1)
return 0;
640#elif defined(HAVE_SYS_PROCCTL_H)
645 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &status) == -1) {
654 if (status != PROC_TRACE_CTL_ENABLE)
return 0;
673#ifdef HAVE_SYS_RESOURCE_H
674 if (getrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
690#ifdef HAVE_SYS_RESOURCE_H
692 struct rlimit current;
697 if (getrlimit(RLIMIT_CORE, ¤t) < 0) {
702 if (allow_core_dumps) {
703 if ((current.rlim_cur != init_core_limit.rlim_cur) ||
704 (current.rlim_max != init_core_limit.rlim_max)) {
705 if (setrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
722 }
else if (current.rlim_cur != 0) {
723 struct rlimit no_core;
725 no_core.rlim_cur = 0;
726 no_core.rlim_max = current.rlim_max;
728 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
739#if defined(HAVE_SYS_PROCCTL_H) || (defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE))
786 fr_strerror_const(
"Failed writing panic_action to temporary buffer (truncated)");
794 if (stat(p, &statbuf) == 0) {
796 if ((statbuf.st_mode & S_IWOTH) != 0) {
814 size_t left =
sizeof(cmd), ret;
835 memset(cmd, 0,
sizeof(cmd));
861 while ((q = strstr(p,
"%p"))) {
862 out += ret =
snprintf(
out, left,
"%.*s%d", (
int) (q - p), p, (
int) getpid());
871 if (strlen(p) >= left)
goto oob;
875 bool disable =
false;
975#define TALLOC_REPORT_MAX_DEPTH 20
985 log = fdopen(fd,
"w");
993 fprintf(log,
"Current state of talloced memory:\n");
998 fprintf(log,
"Talloc chunk lineage:\n");
999 fprintf(log,
"%p (%s)", ctx, talloc_get_name(ctx));
1003 fprintf(log,
" < %p (%s)", ctx, talloc_get_name(ctx));
1010 fprintf(log,
"Talloc context level %i:\n", i++);
1011 talloc_report_full(ctx, log);
1012 }
while ((ctx = talloc_parent(ctx)) &&
1025 talloc_disable_null_tracking();
1039 marker = talloc(ctx,
bool);
1069 static bool setup =
false;
1074 char const *p = cmd;
1081 while ((q = strstr(p,
"%e"))) {
1091 if (strlen(p) >= left)
goto oob;
1111 env = getenv(
"DEBUGGER_ATTACHED");
1112 if (env && (strcmp(env,
"yes") == 0)) {
1115 }
else if (env && (strcmp(env,
"no") == 0)) {
1171#if defined(HAVE_MALLOPT) && !defined(NDEBUG)
1178 if (!getenv(
"TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
1180# ifdef M_CHECK_ACTION
1181 mallopt(M_CHECK_ACTION, 3);
1227 char buffer[(0x10 * 3) + 1];
1230 for (i = 0; i < data_len; i += 0x10) {
1232 if ((i + len) > data_len) len = data_len - i;
1234 for (p =
buffer, j = 0; j < len; j++, p += 3)
snprintf(p, end - p,
"%02x ",
data[i + j]);
1328 if (status != EXIT_SUCCESS) {
1331 if (error && *error && (status != 0)) {
1332 FR_FAULT_LOG(
"%sEXIT(%i) CALLED %s[%d]. Last error was: %s", now ?
"_" :
"",
1341 if (now) _Exit(status);
1349 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.
static TALLOC_CTX * talloc_autofree_ctx
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.
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.
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