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"
127#if defined(__APPLE__)
129 "leak:ImageLoaderMachO::doImageInit\n"
130 "leak:_st_tzset_basic\n"
131 "leak:attachCategories\n"
135 "leak:libSystem_atfork_child\n"
136 "leak:libsystem_notify\n"
140 "leak:perl_construct\n"
141 "leak:realizeClassWithoutSwift\n"
143 "leak:tzsetwall_basic\n"
144#elif defined(__linux__)
145 "leak:*getpwnam_r*\n"
158char const CC_HINT(
used) *__lsan_default_options(
void)
160 return "print_suppressions=0";
166int CC_HINT(
used) __lsan_is_turned_off(
void)
171 if (lsan_disable)
return 1;
174 if (lsan_test_pid != 0)
return 0;
177 if (write(lsan_test_pipe[1], &ret,
sizeof(ret)) < 0) {
178 fprintf(stderr,
"Writing LSAN status failed: %s",
fr_syserror(errno));
180 close(lsan_test_pipe[1]);
197 if (lsan_state != INT_MAX)
return lsan_state;
199 if (pipe(lsan_test_pipe) < 0) {
204 lsan_test_pid = fork();
205 if (lsan_test_pid == -1) {
211 if (lsan_test_pid == 0) {
212 close(lsan_test_pipe[0]);
217 close(lsan_test_pipe[1]);
219 while ((read(lsan_test_pipe[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
221 close(lsan_test_pipe[0]);
224 waitpid(lsan_test_pid, NULL, 0);
238#if defined(HAVE_SYS_PROCCTL_H)
243 if (procctl(P_PID, getpid(), PROC_TRACE_STATUS, &status) == -1) {
261#elif defined(__APPLE__)
276 struct kinfo_proc info;
283 info.kp_proc.p_flag = 0;
291 mib[2] = KERN_PROC_PID;
296 ret = sysctl(mib,
NUM_ELEMENTS(mib), &info, &size, NULL, 0);
297 if (ret != 0)
return -1;
300 return ((info.kp_proc.p_flag & P_TRACED) != 0);
302#elif defined(HAVE_SYS_PTRACE_H) && !defined(__EMSCRIPTEN__)
315 int from_child[2] = {-1, -1};
317#ifdef HAVE_CAPABILITY_H
318 cap_flag_value_t state;
326 caps = cap_get_proc();
332 if (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_PERMITTED, &state) < 0) {
339 if ((state == CAP_SET) && (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &state) < 0)) {
349 if (state == CAP_CLEAR) {
350 fr_strerror_printf(
"ptrace capability not set. If debugger detection is required run as root or: "
351 "setcap cap_sys_ptrace+ep <path_to_binary>");
358 if (pipe(from_child) < 0) {
372 int ppid = getppid();
379#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
385DIAG_ON(deprecated-declarations)
388 close(from_child[0]);
399 _PTRACE(flags, ppid);
406 if (write(from_child[1], &ret,
sizeof(ret)) < 0) {
407 fprintf(stderr,
"Writing ptrace status to parent failed: %s\n",
fr_syserror(errno));
411 _PTRACE_DETACH(ppid);
444 }
else if (errno == EPERM) {
454 fprintf(stderr,
"Debugger check failed to attach to parent with unexpected error: %s\n",
fr_syserror(errno));
466 while ((read(from_child[0], &ret,
sizeof(ret)) < 0) && (errno == EINTR));
469 close(from_child[1]);
470 close(from_child[0]);
515 return "Debug state unknown (ptrace functionality not available)";
518 return "Debug state unknown (cap_sys_ptrace capability not set)";
521 return "Debug state unknown";
524 return "Found debugger attached";
527 return "Debugger not attached";
542 if (always) raise(SIGTRAP);
546 fprintf(stderr,
"Debugger detected, raising SIGTRAP\n");
568 ptr = talloc(ctx,
char);
576#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) && !defined(__EMSCRIPTEN__)
579 if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
587#elif defined(HAVE_SYS_PROCCTL_H)
590 int mode = dumpable ? PROC_TRACE_CTL_ENABLE : PROC_TRACE_CTL_DISABLE;
592 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode) == -1) {
611#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE) && !defined(__EMSCRIPTEN__)
616 ret = prctl(PR_GET_DUMPABLE);
625 if (ret != 1)
return 0;
628#elif defined(HAVE_SYS_PROCCTL_H)
633 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &status) == -1) {
642 if (status != PROC_TRACE_CTL_ENABLE)
return 0;
661#ifdef HAVE_SYS_RESOURCE_H
662 if (getrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
678#ifdef HAVE_SYS_RESOURCE_H
685 if (getrlimit(RLIMIT_CORE, &
current) < 0) {
690 if (allow_core_dumps) {
691 if ((
current.rlim_cur != init_core_limit.rlim_cur) ||
692 (
current.rlim_max != init_core_limit.rlim_max)) {
693 if (setrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
710 }
else if (
current.rlim_cur != 0) {
711 struct rlimit no_core;
713 no_core.rlim_cur = 0;
714 no_core.rlim_max =
current.rlim_max;
716 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
727#if defined(HAVE_SYS_PROCCTL_H) || (defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE))
774 fr_strerror_const(
"Failed writing panic_action to temporary buffer (truncated)");
782 if (stat(p, &statbuf) == 0) {
784 if ((statbuf.st_mode & S_IWOTH) != 0) {
802 size_t left =
sizeof(cmd), ret;
823 memset(cmd, 0,
sizeof(cmd));
849 while ((q = strstr(p,
"%p"))) {
850 out += ret =
snprintf(
out, left,
"%.*s%d", (
int) (q - p), p, (
int) getpid());
859 if (strlen(p) >= left)
goto oob;
863 bool disable =
false;
963#define TALLOC_REPORT_MAX_DEPTH 20
973 log = fdopen(fd,
"w");
981 fprintf(log,
"Current state of talloced memory:\n");
986 fprintf(log,
"Talloc chunk lineage:\n");
987 fprintf(log,
"%p (%s)", ctx, talloc_get_name(ctx));
991 fprintf(log,
" < %p (%s)", ctx, talloc_get_name(ctx));
998 fprintf(log,
"Talloc context level %i:\n", i++);
999 talloc_report_full(ctx, log);
1000 }
while ((ctx = talloc_parent(ctx)) &&
1013 talloc_disable_null_tracking();
1027 marker = talloc(ctx,
bool);
1057 static bool setup =
false;
1062 char const *p = cmd;
1069 while ((q = strstr(p,
"%e"))) {
1079 if (strlen(p) >= left)
goto oob;
1099 env = getenv(
"DEBUGGER_ATTACHED");
1100 if (env && (strcmp(env,
"yes") == 0)) {
1103 }
else if (env && (strcmp(env,
"no") == 0)) {
1159#if defined(HAVE_MALLOPT) && !defined(NDEBUG)
1166 if (!getenv(
"TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
1168# ifdef M_CHECK_ACTION
1169 mallopt(M_CHECK_ACTION, 3);
1215 char buffer[(0x10 * 3) + 1];
1218 for (i = 0; i < data_len; i += 0x10) {
1220 if ((i + len) > data_len) len = data_len - i;
1222 for (p =
buffer, j = 0; j < len; j++, p += 3)
snprintf(p, end - p,
"%02x ",
data[i + j]);
1316 if (status != EXIT_SUCCESS) {
1319 if (error && *error && (status != 0)) {
1320 FR_FAULT_LOG(
"%sEXIT(%i) CALLED %s[%d]. Last error was: %s", now ?
"_" :
"",
1329 if (now) _Exit(status);
1337 if (now) _Exit(status);
1353 if ((size + 4) == offset) {
vsnprintf(buffer, sizeof(buffer), fmt, args)
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.
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
static uint32_t fr_hash_struct(void const *ptr, size_t size, size_t offset)
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.
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.
#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,...)
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
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)
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 rc_request_t * current
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