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);
 
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.
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.
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
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