The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
debug.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Functions to help with debugging
18 *
19 * @file src/lib/util/debug.c
20 *
21 * @copyright 2013 The FreeRADIUS server project
22 * @copyright 2013 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
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>
29
30#include <pthread.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <sys/stat.h>
34#include <sys/wait.h>
35
36#if defined(HAVE_MALLOPT) && defined(HAVE_MALLOC_H)
37# include <malloc.h>
38#endif
39
40#ifdef HAVE_SYS_PRCTL_H
41# include <sys/prctl.h>
42#endif
43
44#ifdef HAVE_SYS_PROCCTL_H
45# include <sys/procctl.h>
46#endif
47
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
52# endif
53# if !defined(PT_DETACH) && defined(PTRACE_DETACH)
54# define PT_DETACH PTRACE_DETACH
55# endif
56#endif
57
58#ifdef HAVE_SYS_RESOURCE_H
59# include <sys/resource.h>
60#endif
61
62#ifdef __APPLE__
63#include <sys/sysctl.h>
64#endif
65
66static char panic_action[512]; //!< The command to execute when panicking.
67static fr_fault_cb_t panic_cb = NULL; //!< Callback to execute whilst panicking, before the
68 //!< panic_action.
69
70static bool dump_core; //!< Whether we should drop a core on fatal signals.
71
72int fr_fault_log_fd = STDERR_FILENO; //!< Where to write debug output.
73
74fr_debug_state_t fr_debug_state = DEBUGGER_STATE_UNKNOWN; //!< Whether we're attached to by a debugger.
75
76#ifdef HAVE_SYS_RESOURCE_H
77static struct rlimit init_core_limit;
78#endif
79
80/*
81 * On BSD systems, ptrace(PT_DETACH) uses a third argument for
82 * resume address, with the magic value (void *)1 to resume where
83 * process stopped. Specifying NULL there leads to a crash because
84 * process resumes at address 0.
85 */
86#if defined(HAVE_SYS_PTRACE_H)
87# ifdef __linux__
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)
93#endif
94
95# ifdef HAVE_CAPABILITY_H
96# include <sys/capability.h>
97# endif
98#endif
99
100#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
101# include <sanitizer/lsan_interface.h>
102#endif
103
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; //!< Explicitly disable LSAN
109
110/*
111 * Some versions of lsan_interface.h are broken and don't declare
112 * the prototypes of the functions properly, omitting the zero argument
113 * specifier (void), so we need to disable the warning.
114 *
115 * Observed with clang 5.
116 */
117DIAG_OFF(missing-prototypes)
118/** Callback for LSAN - do not rename
119 *
120 */
121char const CC_HINT(used) *__lsan_default_suppressions(void)
122{
123 return
124 "leak:CRYPTO_THREAD_lock_new\n" /* OpenSSL init leak - reported by heaptrack */
125 /* librdkafka initialises Cyrus SASL on first rd_kafka_new(), which dlopens
126 * libsasl2 and never unloads it. Not an rlm_kafka bug. */
127 "leak:rd_kafka_sasl_cyrus_global_init\n"
128#if defined(__APPLE__)
129 "leak:*gmtsub*\n"
130 "leak:ImageLoaderMachO::doImageInit\n"
131 "leak:initializeNonMetaClass\n"
132 "leak:_st_tzset_basic\n"
133 "leak:attachCategories\n"
134 "leak:fork\n"
135 "leak:getaddrinfo\n"
136 "leak:getpwuid_r\n"
137 "leak:libSystem_atfork_child\n"
138 "leak:libsystem_notify\n"
139 "leak:load_images\n"
140 "leak:newlocale\n"
141 /* Perl >= 5.32.0 - Upstream bug, tracked by https://github.com/Perl/perl5/issues/18108 */
142 "leak:perl_construct\n"
143 "leak:realizeClassWithoutSwift\n"
144 "leak:tzset\n"
145 "leak:tzsetwall_basic\n"
146 /* macOS Objective-C runtime and CoreFoundation lazily allocate
147 * metadata on first class touch (e.g. inside libsasl2 which
148 * librdkafka dlopens, or on dispatch-apply worker threads).
149 * These stacks have no symbols, only library paths; suppress by
150 * path substring since the process-wide state is reclaimed on
151 * exit by the OS. */
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" /* libc startup leak - reported by heaptrack */
157 "leak:_dl_init\n" /* dl startup leak - reported by heaptrack */
158 "leak:initgroups\n" /* libc startup leak - reported by heaptrack */
159 "leak:kqueue\n"
160 /* libdigestmd5 (cyrus-sasl) calls EVP_CIPHER_fetch on OpenSSL 3.x
161 * but never calls EVP_CIPHER_free. Third-party bug; not ours to fix. */
162 "leak:libdigestmd5\n"
163#endif
164 ;
165}
166
167/** Callback for LSAN - do not rename
168 *
169 * Turn off suppressions by default as it interferes with interpreting
170 * output from some of the test utilities.
171 */
172char const CC_HINT(used) *__lsan_default_options(void)
173{
174 return "print_suppressions=0";
175}
176
177/** Callback for LSAN - do not rename
178 *
179 */
180int CC_HINT(used) __lsan_is_turned_off(void)
181{
182 uint8_t ret = 1;
183
184 /* Disable LSAN explicitly - Used for tests involving fork() */
185 if (lsan_disable) return 1;
186
187 /* Parent */
188 if (lsan_test_pid != 0) return 0;
189
190 /* Child */
191 if (write(lsan_test_pipe[1], &ret, sizeof(ret)) < 0) {
192 fprintf(stderr, "Writing LSAN status failed: %s", fr_syserror(errno));
193 }
194 close(lsan_test_pipe[1]);
195 return 0;
196}
197DIAG_ON(missing-prototypes)
198
199/** Determine if we're running under LSAN (Leak Sanitizer)
200 *
201 * @return
202 * - 0 if we're not.
203 * - 1 if we are.
204 * - -1 if we can't tell because of an error.
205 * - -2 if we can't tell because we were compiled with support for the LSAN interface.
206 */
207int fr_get_lsan_state(void)
208{
209 uint8_t ret = 0;
210
211 if (lsan_state != INT_MAX) return lsan_state;/* Use cached result */
212
213 if (pipe(lsan_test_pipe) < 0) {
214 fr_strerror_printf("Failed opening internal pipe: %s", fr_syserror(errno));
215 return -1;
216 }
217
218 lsan_test_pid = fork();
219 if (lsan_test_pid == -1) {
220 fr_strerror_printf("Error forking: %s", fr_syserror(errno));
221 return -1;
222 }
223
224 /* Child */
225 if (lsan_test_pid == 0) {
226 close(lsan_test_pipe[0]); /* Close parent's side */
227 exit(EXIT_SUCCESS); /* Results in LSAN calling __lsan_is_turned_off via onexit handler */
228 }
229
230 /* Parent */
231 close(lsan_test_pipe[1]); /* Close child's side */
232
233 while ((read(lsan_test_pipe[0], &ret, sizeof(ret)) < 0) && (errno == EINTR));
234
235 close(lsan_test_pipe[0]); /* Close our side (so we don't leak FDs) */
236
237 /* Collect child */
238 waitpid(lsan_test_pid, NULL, 0);
239
240 lsan_state = ret; /* Cache test results */
241
242 return ret;
243}
244#else
246{
247 fr_strerror_const("Not built with support for LSAN interface");
248 return -2;
249}
250#endif
251
252#if defined(HAVE_SYS_PROCCTL_H)
253int fr_get_debug_state(void)
254{
255 int status;
256
257 if (procctl(P_PID, getpid(), PROC_TRACE_STATUS, &status) == -1) {
258 fr_strerror_printf("Cannot get dumpable flag: procctl(PROC_TRACE_STATUS) failed: %s", fr_syserror(errno));
260 }
261
262 /*
263 * As FreeBSD docs say about "PROC_TRACE_STATUS":
264 *
265 * Returns the current tracing status for the specified process in the
266 * integer variable pointed to by data. If tracing is disabled, data
267 * is set to -1. If tracing is enabled, but no debugger is attached by
268 * the ptrace(2) syscall, data is set to 0. If a debugger is attached,
269 * data is set to the pid of the debugger process.
270 */
271 if (status <= 0) return DEBUGGER_STATE_NOT_ATTACHED;
272
274}
275#elif defined(__APPLE__)
276/** The ptrace_attach() method no longer works as of macOS 11.4 (we always get eperm)
277 *
278 * Apple published this helpful article here which provides the
279 * magical invocation: https://developer.apple.com/library/archive/qa/qa1361/_index.html
280 *
281 * @return
282 * - 0 if we're not.
283 * - 1 if we are.
284 * - -1
285 */
286int fr_get_debug_state(void)
287{
288 int ret;
289 int mib[4];
290 struct kinfo_proc info;
291 size_t size;
292
293 /*
294 * Initialize the flags so that, if sysctl fails for some
295 * reason, we get a predictable result.
296 */
297 info.kp_proc.p_flag = 0;
298
299 /*
300 * Initialize mib, which tells sysctl the info we want, in this case
301 * we're looking for information about a specific process ID.
302 */
303 mib[0] = CTL_KERN;
304 mib[1] = KERN_PROC;
305 mib[2] = KERN_PROC_PID;
306 mib[3] = getpid();
307
308 /* Call sysctl */
309 size = sizeof(info);
310 ret = sysctl(mib, NUM_ELEMENTS(mib), &info, &size, NULL, 0);
311 if (ret != 0) return -1;
312
313 /* We're being debugged if the P_TRACED flag is set */
314 return ((info.kp_proc.p_flag & P_TRACED) != 0);
315}
316#elif defined(HAVE_SYS_PTRACE_H) && !defined(__EMSCRIPTEN__)
317/** Determine if we're running under a debugger by attempting to attach using pattach
318 *
319 * @return
320 * - 0 if we're not.
321 * - 1 if we are.
322 * - -1 if we can't tell because of an error.
323 * - -2 if we can't tell because we don't have the CAP_SYS_PTRACE capability.
324 */
325int fr_get_debug_state(void)
326{
327 int pid;
328
329 int from_child[2] = {-1, -1};
330
331#ifdef HAVE_CAPABILITY_H
332 cap_flag_value_t state;
333 cap_t caps;
334
335 /*
336 * If we're running under linux, we first need to check if we have
337 * permission to to ptrace. We do that using the capabilities
338 * functions.
339 */
340 caps = cap_get_proc();
341 if (!caps) {
342 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
344 }
345
346 if (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_PERMITTED, &state) < 0) {
347 fr_strerror_printf("Failed getting CAP_SYS_PTRACE permitted state: %s",
348 fr_syserror(errno));
349 cap_free(caps);
351 }
352
353 if ((state == CAP_SET) && (cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &state) < 0)) {
354 fr_strerror_printf("Failed getting CAP_SYS_PTRACE effective state: %s",
355 fr_syserror(errno));
356 cap_free(caps);
358 }
359
360 /*
361 * We don't have permission to ptrace, so this test will always fail.
362 */
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>");
366 cap_free(caps);
368 }
369 cap_free(caps);
370#endif
371
372 if (pipe(from_child) < 0) {
373 fr_strerror_printf("Error opening internal pipe: %s", fr_syserror(errno));
375 }
376
377 pid = fork();
378 if (pid == -1) {
379 fr_strerror_printf("Error forking: %s", fr_syserror(errno));
381 }
382
383 /* Child */
384 if (pid == 0) {
385 int8_t ret = DEBUGGER_STATE_NOT_ATTACHED;
386 int ppid = getppid();
387
388 /*
389 * Disable the leak checker for this forked process
390 * so we don't get spurious leaks reported.
391 */
392#ifdef HAVE_SANITIZER_LSAN_INTERFACE_H
393 lsan_disable = true;
394#endif
395
396 /* Close parent's side */
397 close(from_child[0]);
398
399 /*
400 * FreeBSD is extremely picky about the order of operations here
401 * we need to attach, wait *then* write whilst the parent is still
402 * suspended, then detach, continuing the process.
403 *
404 * If we don't do it in that order the read in the parent triggers
405 * a SIGKILL.
406 */
407 errno = 0;
408DIAG_OFF(deprecated-declarations)
409 _PTRACE(PT_ATTACH, ppid);
410DIAG_ON(deprecated-declarations)
411 if (errno == 0) {
412 /* Wait for the parent to stop */
413 waitpid(ppid, NULL, 0);
414
415 /* Tell the parent what happened */
416 send_status:
417 if (write(from_child[1], &ret, sizeof(ret)) < 0) {
418 fprintf(stderr, "Writing ptrace status to parent failed: %s\n", fr_syserror(errno));
419 }
420
421 /* Detach */
422 _PTRACE_DETACH(ppid);
423
424
425 /*
426 * We call _exit() instead of exit(). This means that we skip the atexit() handlers,
427 * which don't need to run in a temporary child process. Skipping them means that we
428 * avoid dirtying those pages to "clean things up", which is then immediately followed by
429 * exiting.
430 *
431 * Skipping the atexit() handlers also means that we're not worried about memory leaks
432 * because things "aren't cleaned up correctly". We're not exiting cleanly here (and
433 * don't care to exit cleanly). So just exiting with no cleanups is fine.
434 */
435 _exit(0); /* don't run the atexit() handlers. */
436 /*
437 * man ptrace says the following:
438 *
439 * EPERM The specified process cannot be traced. This could be
440 * because the tracer has insufficient privileges (the
441 * required capability is CAP_SYS_PTRACE); unprivileged
442 * processes cannot trace processes that they cannot send
443 * signals to or those running set-user-ID/set-group-ID
444 * programs, for obvious reasons. Alternatively, the process
445 * may already be being traced, or (before Linux 2.6.26) be
446 * init(1) (PID 1).
447 *
448 * In any case, we are very unlikely to be able to attach to
449 * the process from the panic action.
450 *
451 * We checked for CAP_SYS_PTRACE previously, so know that
452 * we _should_ haven been ablle to attach, so if we can't, it's
453 * likely that we're already being traced.
454 */
455 } else if (errno == EPERM) {
457 goto send_status;
458 }
459
460 /*
461 * Unexpected error, we don't know whether we're already running
462 * under a debugger or not...
463 */
465 fprintf(stderr, "Debugger check failed to attach to parent with unexpected error: %s\n", fr_syserror(errno));
466 goto send_status;
467 /* Parent */
468 } else {
469 int8_t ret = DEBUGGER_STATE_UNKNOWN;
470
471 /*
472 * The child writes errno (reason) if pattach failed else 0.
473 *
474 * This read may be interrupted by pattach,
475 * which is why we need the loop.
476 */
477 while ((read(from_child[0], &ret, sizeof(ret)) < 0) && (errno == EINTR));
478
479 /* Close the pipes here (if we did it above, it might race with pattach) */
480 close(from_child[1]);
481 close(from_child[0]);
482
483 /* Collect the status of the child */
484 waitpid(pid, NULL, 0);
485
486 return ret;
487 }
488}
489#else
491{
492 fr_strerror_const("PTRACE not available");
493
495}
496#endif
497
498/** Should be run before using setuid or setgid to get useful results
499 *
500 * @note sets the fr_debug_state global.
501 */
503{
505
506#ifndef NDEBUG
507 /*
508 * There are many reasons why this might happen with
509 * a vanilla install, so we don't want to spam users
510 * with messages they won't understand and may not
511 * want to resolve.
512 */
513 if (fr_debug_state < 0) fprintf(stderr, "Getting debug state failed: %s\n", fr_strerror());
514#endif
515}
516
517/** Return current value of debug_state
518 *
519 * @param state to translate into a humanly readable value.
520 * @return humanly readable version of debug state.
521 */
523{
524 switch (state) {
526 return "Debug state unknown (ptrace functionality not available)";
527
529 return "Debug state unknown (cap_sys_ptrace capability not set)";
530
532 return "Debug state unknown";
533
535 return "Found debugger attached";
536
538 return "Debugger not attached";
539 }
540
541 return "<INVALID>";
542}
543
544/** Break in debugger (if were running under a debugger)
545 *
546 * If the server is running under a debugger this will raise a
547 * SIGTRAP which will pause the running process.
548 *
549 * If the server is not running under debugger then this will do nothing.
550 */
551void fr_debug_break(bool always)
552{
553 if (always) raise(SIGTRAP);
554
557 fprintf(stderr, "Debugger detected, raising SIGTRAP\n");
558 fflush(stderr);
559
560 raise(SIGTRAP);
561 }
562}
563
564static int _panic_on_free(UNUSED char *foo)
565{
566 fr_fault(SIGABRT);
567 return -1; /* this should make the free fail */
568}
569
570/** Insert memory into the context of another talloc memory chunk which
571 * causes a panic when freed.
572 *
573 * @param ctx TALLOC_CTX to monitor for frees.
574 */
575void fr_panic_on_free(TALLOC_CTX *ctx)
576{
577 char *ptr;
578
579 ptr = talloc(ctx, char);
580 talloc_set_destructor(ptr, _panic_on_free);
581}
582
583/** Set the dumpable flag, also controls whether processes can PATTACH
584 *
585 * @param dumpable whether we should allow core dumping
586 */
587#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) && !defined(__EMSCRIPTEN__)
588static int fr_set_pr_dumpable_flag(bool dumpable)
589{
590 if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
591 fr_strerror_printf("Cannot re-enable core dumps: prctl(PR_SET_DUMPABLE) failed: %s",
592 fr_syserror(errno));
593 return -1;
594 }
595
596 return 0;
597}
598#elif defined(HAVE_SYS_PROCCTL_H)
599static int fr_set_pr_dumpable_flag(bool dumpable)
600{
601 int mode = dumpable ? PROC_TRACE_CTL_ENABLE : PROC_TRACE_CTL_DISABLE;
602
603 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode) == -1) {
604 fr_strerror_printf("Cannot re-enable core dumps: procctl(PROC_TRACE_CTL) failed: %s",
605 fr_syserror(errno));
606 return -1;
607 }
608
609 return 0;
610}
611#else
612static int fr_set_pr_dumpable_flag(UNUSED bool dumpable)
613{
614 fr_strerror_const("Changing value of PR_DUMPABLE not supported on this system");
615 return -2;
616}
617#endif
618
619/** Get the processes dumpable flag
620 *
621 */
622#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE) && !defined(__EMSCRIPTEN__)
623static int fr_get_pr_dumpable_flag(void)
624{
625 int ret;
626
627 ret = prctl(PR_GET_DUMPABLE);
628 if (ret < 0) {
629 fr_strerror_printf("Cannot get dumpable flag: %s", fr_syserror(errno));
630 return -1;
631 }
632
633 /*
634 * Linux is crazy and prctl sometimes returns 2 for disabled
635 */
636 if (ret != 1) return 0;
637 return 1;
638}
639#elif defined(HAVE_SYS_PROCCTL_H)
640static int fr_get_pr_dumpable_flag(void)
641{
642 int status;
643
644 if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &status) == -1) {
645 fr_strerror_printf("Cannot get dumpable flag: procctl(PROC_TRACE_CTL) failed: %s", fr_syserror(errno));
646 return -1;
647 }
648
649 /*
650 * There are a few different kinds of disabled, but only
651 * one ENABLE.
652 */
653 if (status != PROC_TRACE_CTL_ENABLE) return 0;
654
655 return 1;
656}
657#else
659{
660 fr_strerror_const("Getting value of PR_DUMPABLE not supported on this system");
661 return -2;
662}
663#endif
664
665
666/** Get the current maximum for core files
667 *
668 * Do this before anything else so as to ensure it's properly initialized.
669 */
671{
672#ifdef HAVE_SYS_RESOURCE_H
673 if (getrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
674 fr_strerror_printf("Failed to get current core limit: %s", fr_syserror(errno));
675 return -1;
676 }
677#endif
678 return 0;
679}
680
681/** Enable or disable core dumps
682 *
683 * @param allow_core_dumps whether to enable or disable core dumps.
684 */
685int fr_set_dumpable(bool allow_core_dumps)
686{
687 dump_core = allow_core_dumps;
688
689#ifdef HAVE_SYS_RESOURCE_H
690 {
691 struct rlimit current;
692
693 /*
694 * Reset the core limits (or disable them)
695 */
696 if (getrlimit(RLIMIT_CORE, &current) < 0) {
697 fr_strerror_printf("Failed to get current core limit: %s", fr_syserror(errno));
698 return -1;
699 }
700
701 if (allow_core_dumps) {
702 if ((current.rlim_cur != init_core_limit.rlim_cur) ||
703 (current.rlim_max != init_core_limit.rlim_max)) {
704 if (setrlimit(RLIMIT_CORE, &init_core_limit) < 0) {
705 fr_strerror_printf("Cannot update core dump limit: %s", fr_syserror(errno));
706
707 return -1;
708 }
709 }
710 /*
711 * We've been told to disable core dumping,
712 * rlim_cur is not set to zero.
713 *
714 * Set rlim_cur to zero, but leave rlim_max
715 * set to whatever the current value is.
716 *
717 * This is because, later, we may need to
718 * re-enable core dumps to allow the debugger
719 * to attach *sigh*.
720 */
721 } else if (current.rlim_cur != 0) {
722 struct rlimit no_core;
723
724 no_core.rlim_cur = 0;
725 no_core.rlim_max = current.rlim_max;
726
727 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
728 fr_strerror_printf("Failed disabling core dumps: %s", fr_syserror(errno));
729
730 return -1;
731 }
732 }
733 }
734#endif
735 /*
736 * Macro needed so we don't emit spurious errors
737 */
738#if defined(HAVE_SYS_PROCCTL_H) || (defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE))
739 if (fr_set_pr_dumpable_flag(allow_core_dumps) < 0) return -1;
740#endif
741
742 return 0;
743}
744
745/** Reset dumpable state to previously configured value
746 *
747 * Needed after suid up/down
748 *
749 * @return
750 * - 0 on success.
751 * - -1 on failure.
752 */
754{
756}
757
758/** Check to see if panic_action file is world writable
759 *
760 * @return
761 * - 0 if file is OK.
762 * - -1 if the file is world writable.
763 */
765{
766 char const *p, *q;
767 size_t len;
768 char filename[256];
769 struct stat statbuf;
770
771 /*
772 * Try and guess which part of the command is the binary, and check to see if
773 * it's world writable, to try and save the admin from their own stupidity.
774 *
775 * @fixme we should do this properly and take into account single and double
776 * quotes.
777 */
778 if ((q = strchr(panic_action, ' '))) {
779 /*
780 * need to use a static buffer, because allocing memory in a signal handler
781 * is a bad idea and can result in deadlock.
782 */
783 len = snprintf(filename, sizeof(filename), "%.*s", (int)(q - panic_action), panic_action);
784 if (is_truncated(len, sizeof(filename))) {
785 fr_strerror_const("Failed writing panic_action to temporary buffer (truncated)");
786 return -1;
787 }
788 p = filename;
789 } else {
790 p = panic_action;
791 }
792
793 if (stat(p, &statbuf) == 0) {
794#ifdef S_IWOTH
795 if ((statbuf.st_mode & S_IWOTH) != 0) {
796 fr_strerror_printf("panic_action file \"%s\" is globally writable", p);
797 return -1;
798 }
799#endif
800 }
801
802 return 0;
803}
804
805/** Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
806 *
807 * @param sig caught
808 */
810{
811 char cmd[sizeof(panic_action) + 20];
812 char *out = cmd;
813 size_t left = sizeof(cmd), ret;
814
815 char const *p = panic_action;
816 char const *q;
817
818 int code;
819
820 /*
821 * If a debugger is attached, we don't want to run the panic action,
822 * as it may interfere with the operation of the debugger.
823 * If something calls us directly we just raise the signal and let
824 * the debugger handle it how it wants.
825 */
827 FR_FAULT_LOG("RAISING SIGNAL: %s", strsignal(sig));
828 raise(sig);
829 }
830
831 /*
832 * Makes the backtraces slightly cleaner
833 */
834 memset(cmd, 0, sizeof(cmd));
835
836 FR_FAULT_LOG("CAUGHT SIGNAL: %s", strsignal(sig));
837
838 /*
839 * Run the callback if one was registered
840 */
841 if (panic_cb && (panic_cb(sig) < 0)) goto finish;
842
843 fr_backtrace();
844
845 /* No panic action set... */
846 if (panic_action[0] == '\0') {
847 FR_FAULT_LOG("No panic action set");
848 goto finish;
849 }
850
851 /*
852 * Check for administrator sanity.
853 */
854 if (fr_fault_check_permissions() < 0) {
855 FR_FAULT_LOG("Refusing to execute panic action: %s", fr_strerror());
856 goto finish;
857 }
858
859 /* Substitute %p for the current PID (useful for attaching a debugger) */
860 while ((q = strstr(p, "%p"))) {
861 out += ret = snprintf(out, left, "%.*s%d", (int) (q - p), p, (int) getpid());
862 if (left <= ret) {
863 oob:
864 FR_FAULT_LOG("Panic action too long");
865 fr_exit_now(128 + sig);
866 }
867 left -= ret;
868 p = q + 2;
869 }
870 if (strlen(p) >= left) goto oob;
871 strlcpy(out, p, left);
872
873 {
874 bool disable = false;
875
876 FR_FAULT_LOG("Calling: %s", cmd);
877
878 /*
879 * Here we temporarily enable the dumpable flag so if GBD or LLDB
880 * is called in the panic_action, they can pattach to the running
881 * process.
882 */
883 if (fr_get_pr_dumpable_flag() == 0) {
884 if ((fr_set_pr_dumpable_flag(true) < 0) || !fr_get_pr_dumpable_flag()) {
885 FR_FAULT_LOG("Failed setting dumpable flag, pattach may not work: %s", fr_strerror());
886 } else {
887 disable = true;
888 }
889 FR_FAULT_LOG("Temporarily setting PR_DUMPABLE to 1");
890 }
891
892 code = system(cmd);
893
894 /*
895 * We only want to error out here, if dumpable was originally disabled
896 * and we managed to change the value to enabled, but failed
897 * setting it back to disabled.
898 */
899 if (disable) {
900 FR_FAULT_LOG("Resetting PR_DUMPABLE to 0");
901 if (fr_set_pr_dumpable_flag(false) < 0) {
902 FR_FAULT_LOG("Failed resetting dumpable flag to off: %s", fr_strerror());
903 FR_FAULT_LOG("Exiting due to insecure process state");
904 fr_exit_now(EXIT_FAILURE);
905 }
906 }
907
908 FR_FAULT_LOG("Panic action exited with %i", code);
909
910 fr_exit_now(128 + sig);
911 }
912
913finish:
914 /*
915 * (Re-)Raise the signal, so that if we're running under
916 * a debugger.
917 *
918 * This allows debuggers to function normally and catch
919 * fatal signals.
920 */
921 fr_unset_signal(sig); /* Make sure we don't get into a loop */
922 raise(sig);
923 fr_exit_now(128 + sig); /* Function marked as noreturn */
924}
925
926/** Callback executed on fatal talloc error
927 *
928 * This is the simple version which mostly behaves the same way as the default
929 * one, and will not call panic_action.
930 *
931 * @param reason string provided by talloc.
932 */
933static void _fr_talloc_fault_simple(char const *reason) CC_HINT(noreturn);
934static void _fr_talloc_fault_simple(char const *reason)
935{
936 FR_FAULT_LOG("talloc abort: %s\n", reason);
937
938 fr_backtrace();
939 abort();
940}
941
942/** Callback executed on fatal talloc error
943 *
944 * Translates a talloc abort into a fr_fault call.
945 * Mostly to work around issues with some debuggers not being able to
946 * attach after a SIGABRT has been raised.
947 *
948 * @param reason string provided by talloc.
949 */
950static void _fr_talloc_fault(char const *reason) CC_HINT(noreturn);
951static void _fr_talloc_fault(char const *reason)
952{
953 FR_FAULT_LOG("talloc abort: %s", reason);
954#ifdef SIGABRT
955 fr_fault(SIGABRT);
956#endif
957 fr_exit_now(128 + SIGABRT);
958}
959
960/** Wrapper to pass talloc log output to our fr_fault_log function
961 *
962 */
963static void _fr_talloc_log(char const *msg)
964{
965 fr_fault_log("%s\n", msg);
966}
967
968/** Generate a talloc memory report for a context and print to stderr/stdout
969 *
970 * @param ctx to generate a report for, may be NULL in which case the root context is used.
971 */
972int fr_log_talloc_report(TALLOC_CTX const *ctx)
973{
974#define TALLOC_REPORT_MAX_DEPTH 20
975
976 FILE *log;
977 int fd;
978
979 fd = dup(fr_fault_log_fd);
980 if (fd < 0) {
981 fr_strerror_printf("Couldn't write memory report, failed to dup log fd: %s", fr_syserror(errno));
982 return -1;
983 }
984 log = fdopen(fd, "w");
985 if (!log) {
986 close(fd);
987 fr_strerror_printf("Couldn't write memory report, fdopen failed: %s", fr_syserror(errno));
988 return -1;
989 }
990
991 if (!ctx) {
992 fprintf(log, "Current state of talloced memory:\n");
993 talloc_report_full(talloc_null_ctx(), log);
994 } else {
995 int i;
996
997 fprintf(log, "Talloc chunk lineage:\n");
998 fprintf(log, "%p (%s)", ctx, talloc_get_name(ctx));
999
1000 i = 0;
1001 while ((i < TALLOC_REPORT_MAX_DEPTH) && (ctx = talloc_parent(ctx))) {
1002 fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
1003 i++;
1004 }
1005 fprintf(log, "\n");
1006
1007 i = 0;
1008 do {
1009 fprintf(log, "Talloc context level %i:\n", i++);
1010 talloc_report_full(ctx, log);
1011 } while ((ctx = talloc_parent(ctx)) &&
1013 (talloc_parent(ctx) != talloc_autofree_context_global()) && /* Stop before we hit the autofree ctx */
1014 (talloc_parent(ctx) != talloc_null_ctx())); /* Stop before we hit NULL ctx */
1015 }
1016
1017 fclose(log);
1018
1019 return 0;
1020}
1021
1023{
1024 talloc_disable_null_tracking();
1025 return 0;
1026}
1027
1028/** Disable the null tracking context when a talloc chunk is freed
1029 *
1030 */
1032{
1033 bool *marker;
1034
1035 /*
1036 * Disable null tracking on exit, else valgrind complains
1037 */
1038 marker = talloc(ctx, bool);
1039 talloc_set_destructor(marker, _disable_null_tracking);
1040}
1041
1042/** Register talloc fault handlers
1043 *
1044 * Just register the fault handlers we need to make talloc
1045 * produce useful debugging output.
1046 */
1048{
1049 talloc_set_log_fn(_fr_talloc_log);
1050 talloc_set_abort_fn(_fr_talloc_fault_simple);
1051}
1052
1053/** Registers signal handlers to execute panic_action on fatal signal
1054 *
1055 * May be called multiple time to change the panic_action/program.
1056 *
1057 * @param[in] ctx to allocate autofreeable resources in.
1058 * @param[in] cmd to execute on fault. If present %p will be substituted
1059 * for the parent PID before the command is executed, and %e
1060 * will be substituted for the currently running program.
1061 * @param[in] program Name of program currently executing (argv[0]).
1062 * @param[in] fault_signals Bitmask (bit N = signal N) of signals to install the
1063 * panic-action handler on. Callers pass
1064 * #PANIC_ACTION_SIGNALS unless they need to leave a
1065 * specific signal's handler alone (e.g. the fuzzer
1066 * shares SIGALRM with libFuzzer's own timeout).
1067 * @return
1068 * - 0 on success.
1069 * - -1 on failure.
1070 */
1071int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program, unsigned long fault_signals)
1072{
1073 static bool setup = false;
1074
1075 char *out = panic_action;
1076 size_t left = sizeof(panic_action);
1077
1078 char const *p = cmd;
1079 char const *q;
1080
1081 if (cmd) {
1082 size_t ret;
1083
1084 /* Substitute %e for the current program */
1085 while ((q = strstr(p, "%e"))) {
1086 out += ret = snprintf(out, left, "%.*s%s", (int) (q - p), p, program ? program : "");
1087 if (left <= ret) {
1088 oob:
1089 fr_strerror_const("Panic action too long");
1090 return -1;
1091 }
1092 left -= ret;
1093 p = q + 2;
1094 }
1095 if (strlen(p) >= left) goto oob;
1096 strlcpy(out, p, left);
1097 } else {
1098 *panic_action = '\0';
1099 }
1100
1101 /*
1102 * Check for administrator sanity.
1103 */
1104 if (fr_fault_check_permissions() < 0) return -1;
1105
1106 /* Unsure what the side effects of changing the signal handler mid execution might be */
1107 if (!setup) {
1108 char *env;
1109
1110 /*
1111 * Installing signal handlers interferes with some debugging
1112 * operations. Give the developer control over whether the
1113 * signal handlers are installed or not.
1114 */
1115 env = getenv("DEBUGGER_ATTACHED");
1116 if (env && (strcmp(env, "yes") == 0)) {
1117 fr_debug_state = DEBUGGER_STATE_ATTACHED; /* i.e. disable signal handlers */
1118
1119 } else if (env && (strcmp(env, "no") == 0)) {
1120 fr_debug_state = DEBUGGER_STATE_NOT_ATTACHED; /* i.e. enable signal handlers */
1121
1122 /*
1123 * Figure out if we were started under a debugger
1124 */
1125 } else {
1127 }
1128
1129 talloc_set_log_fn(_fr_talloc_log);
1130
1131 /*
1132 * These signals can't be properly dealt with in the debugger
1133 * if we set our own signal handlers.
1134 */
1135 switch (fr_debug_state) {
1136 default:
1138#ifdef SIGABRT
1139 if (fault_signals & (1UL << SIGABRT)) {
1140 if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1;
1141
1142 /*
1143 * Use this instead of abort so we get a
1144 * full backtrace with broken versions of LLDB
1145 */
1146 talloc_set_abort_fn(_fr_talloc_fault);
1147 }
1148#endif
1149#ifdef SIGILL
1150 if ((fault_signals & (1UL << SIGILL)) &&
1151 (fr_set_signal(SIGILL, fr_fault) < 0)) return -1;
1152#endif
1153#ifdef SIGFPE
1154 if ((fault_signals & (1UL << SIGFPE)) &&
1155 (fr_set_signal(SIGFPE, fr_fault) < 0)) return -1;
1156#endif
1157#ifdef SIGSEGV
1158 if ((fault_signals & (1UL << SIGSEGV)) &&
1159 (fr_set_signal(SIGSEGV, fr_fault) < 0)) return -1;
1160#endif
1161#ifdef SIGBUS
1162 if ((fault_signals & (1UL << SIGBUS)) &&
1163 (fr_set_signal(SIGBUS, fr_fault) < 0)) return -1;
1164#endif
1165#ifdef SIGALRM
1166 /*
1167 * Used by jlibtool to terminate processes
1168 * that have been running too long. The fuzzer
1169 * clears this bit so libFuzzer's own SIGALRM
1170 * timeout handler stays in place.
1171 */
1172 if ((fault_signals & (1UL << SIGALRM)) &&
1173 (fr_set_signal(SIGALRM, fr_fault) < 0)) return -1;
1174#endif
1175 break;
1176
1178 break;
1179 }
1180
1181 /*
1182 * Needed for memory reports
1183 */
1185
1186#if defined(HAVE_MALLOPT) && !defined(NDEBUG)
1187 /*
1188 * If were using glibc malloc > 2.4 this scribbles over
1189 * uninitialised and freed memory, to make memory issues easier
1190 * to track down.
1191 */
1192# ifdef M_PERTURB
1193 if (!getenv("TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
1194# endif
1195# ifdef M_CHECK_ACTION
1196 mallopt(M_CHECK_ACTION, 3);
1197# endif
1198#endif
1200 }
1201 setup = true;
1202
1203 return 0;
1204}
1205
1206/** Set a callback to be called before fr_fault()
1207 *
1208 * @param func to execute. If callback returns < 0
1209 * fr_fault will exit before running panic_action code.
1210 */
1212{
1213 panic_cb = func;
1214}
1215
1216/** Log output to the fr_fault_log_fd
1217 *
1218 * We used to support a user defined callback, which was set to a radlog
1219 * function. Unfortunately, when logging to syslog, syslog would alloc memory
1220 * which would result in a deadlock if fr_fault was triggered from within
1221 * a allocation call.
1222 *
1223 * Now we just write directly to the FD.
1224 */
1225void fr_fault_log(char const *msg, ...)
1226{
1227 va_list ap;
1228
1229 if (fr_fault_log_fd < 0) return;
1230
1231 va_start(ap, msg);
1233 va_end(ap);
1234}
1235
1236/** Print data as a hex block
1237 *
1238 */
1239void fr_fault_log_hex(uint8_t const *data, size_t data_len)
1240{
1241 size_t i, j, len;
1242 char buffer[(0x10 * 3) + 1];
1243 char *p, *end = buffer + sizeof(buffer);
1244
1245 for (i = 0; i < data_len; i += 0x10) {
1246 len = 0x10;
1247 if ((i + len) > data_len) len = data_len - i;
1248
1249 for (p = buffer, j = 0; j < len; j++, p += 3) snprintf(p, end - p, "%02x ", data[i + j]);
1250
1251 dprintf(fr_fault_log_fd, "%04x: %s\n", (unsigned int) i, buffer);
1252 }
1253}
1254
1255/** Set a file descriptor to log memory reports to.
1256 *
1257 * @param fd to write output to.
1258 */
1260{
1261 fr_fault_log_fd = fd;
1262}
1263
1264/** A soft assertion which triggers the fault handler in debug builds
1265 *
1266 * @param[in] file the assertion failed in.
1267 * @param[in] line of the assertion in the file.
1268 * @param[in] expr that was evaluated.
1269 * @param[in] msg Message to print (may be NULL).
1270 * @param[in] ... Arguments for msg string.
1271 * @return the value of cond.
1272 */
1273bool _fr_assert_fail(char const *file, int line, char const *expr, char const *msg, ...)
1274{
1275 if (msg) {
1276 char str[256]; /* Decent compilers won't allocate this unless fmt is !NULL... */
1277 va_list ap;
1278
1279 va_start(ap, msg);
1280 (void)vsnprintf(str, sizeof(str), msg, ap);
1281 va_end(ap);
1282
1283#ifndef NDEBUG
1284 FR_FAULT_LOG("ASSERT FAILED %s[%d]: %s: %s", file, line, expr, str);
1285 fr_fault(SIGABRT);
1286#else
1287 FR_FAULT_LOG("ASSERT WOULD FAIL %s[%d]: %s: %s", file, line, expr, str);
1288 return false;
1289#endif
1290 }
1291
1292#ifndef NDEBUG
1293 FR_FAULT_LOG("ASSERT FAILED %s[%d]: %s", file, line, expr);
1294 fr_fault(SIGABRT);
1295#else
1296 FR_FAULT_LOG("ASSERT WOULD FAIL %s[%d]: %s", file, line, expr);
1297 return false;
1298#endif
1299}
1300
1301/** A fatal assertion which triggers the fault handler in debug builds or exits
1302 *
1303 * @param[in] file the assertion failed in.
1304 * @param[in] line of the assertion in the file.
1305 * @param[in] expr that was evaluated.
1306 * @param[in] msg Message to print (may be NULL).
1307 * @param[in] ... Arguments for msg string.
1308 */
1309void _fr_assert_fatal(char const *file, int line, char const *expr, char const *msg, ...)
1310{
1311 if (msg) {
1312 char str[256]; /* Decent compilers won't allocate this unless fmt is !NULL... */
1313 va_list ap;
1314
1315 va_start(ap, msg);
1316 (void)vsnprintf(str, sizeof(str), msg, ap);
1317 va_end(ap);
1318
1319 FR_FAULT_LOG("FATAL ASSERT %s[%d]: %s: %s", file, line, expr, str);
1320 } else {
1321 FR_FAULT_LOG("FATAL ASSERT %s[%d]: %s", file, line, expr);
1322 }
1323
1324#ifdef NDEBUG
1325 _fr_exit(file, line, 128 + SIGABRT, true);
1326#else
1327 fr_fault(SIGABRT);
1328#endif
1329}
1330
1331/** Exit possibly printing a message about why we're exiting.
1332 *
1333 * @note Use the fr_exit(status) macro instead of calling this function directly.
1334 *
1335 * @param[in] file where fr_exit() was called.
1336 * @param[in] line where fr_exit() was called.
1337 * @param[in] status we're exiting with.
1338 * @param[in] now Exit immediately.
1339 */
1340#ifndef NDEBUG
1341NEVER_RETURNS void _fr_exit(char const *file, int line, int status, bool now)
1342{
1343 if (status != EXIT_SUCCESS) {
1344 char const *error = fr_strerror();
1345
1346 if (error && *error && (status != 0)) {
1347 FR_FAULT_LOG("%sEXIT(%i) CALLED %s[%d]. Last error was: %s", now ? "_" : "",
1348 status, file, line, error);
1349 } else {
1350 FR_FAULT_LOG("%sEXIT(%i) CALLED %s[%d]", now ? "_" : "", status, file, line);
1351 }
1352
1353 fr_debug_break(false); /* If running under GDB we'll break here */
1354 }
1355
1356 if (now) _Exit(status);
1357 exit(status);
1358}
1359#else
1360NEVER_RETURNS void _fr_exit(UNUSED char const *file, UNUSED int line, int status, bool now)
1361{
1362 if (status != EXIT_SUCCESS) fr_debug_break(false); /* If running under GDB we'll break here */
1363
1364 if (now) _Exit(status);
1365 exit(status);
1366}
1367#endif
static int const char char buffer[256]
Definition acutest.h:641
int const char * file
Definition acutest.h:855
va_end(args)
log_entry msg
Definition acutest.h:947
void int line
Definition acutest.h:810
va_start(args, fmt)
void fr_backtrace(void)
Definition backtrace.c:212
void fr_backtrace_init(UNUSED char const *program)
Definition backtrace.c:344
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:413
#define DIAG_ON(_x)
Definition build.h:576
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define DIAG_OFF(_x)
Definition build.h:575
#define CAP_PERMITTED
Definition cap.h:50
#define CAP_EFFECTIVE
Definition cap.h:48
static int fr_fault_check_permissions(void)
Check to see if panic_action file is world writable.
Definition debug.c:764
static char panic_action[512]
The command to execute when panicking.
Definition debug.c:66
char const * fr_debug_state_to_msg(fr_debug_state_t state)
Return current value of debug_state.
Definition debug.c:522
static int _disable_null_tracking(UNUSED bool *p)
Definition debug.c:1022
static int _panic_on_free(UNUSED char *foo)
Definition debug.c:564
void fr_disable_null_tracking_on_free(TALLOC_CTX *ctx)
Disable the null tracking context when a talloc chunk is freed.
Definition debug.c:1031
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.
Definition debug.c:1273
void fr_fault_set_log_fd(int fd)
Set a file descriptor to log memory reports to.
Definition debug.c:1259
static bool dump_core
Whether we should drop a core on fatal signals.
Definition debug.c:70
int fr_log_talloc_report(TALLOC_CTX const *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
Definition debug.c:972
int fr_set_dumpable(bool allow_core_dumps)
Enable or disable core dumps.
Definition debug.c:685
static void _fr_talloc_fault_simple(char const *reason)
Callback executed on fatal talloc error.
Definition debug.c:934
static int fr_set_pr_dumpable_flag(UNUSED bool dumpable)
Set the dumpable flag, also controls whether processes can PATTACH.
Definition debug.c:612
static fr_fault_cb_t panic_cb
Callback to execute whilst panicking, before the panic_action.
Definition debug.c:67
int fr_fault_log_fd
Where to write debug output.
Definition debug.c:72
int fr_reset_dumpable(void)
Reset dumpable state to previously configured value.
Definition debug.c:753
#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.
Definition debug.c:809
int fr_get_lsan_state(void)
Definition debug.c:245
void fr_debug_state_store(void)
Should be run before using setuid or setgid to get useful results.
Definition debug.c:502
NEVER_RETURNS void _fr_exit(char const *file, int line, int status, bool now)
Exit possibly printing a message about why we're exiting.
Definition debug.c:1341
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition debug.c:1047
void fr_debug_break(bool always)
Break in debugger (if were running under a debugger)
Definition debug.c:551
void fr_fault_set_cb(fr_fault_cb_t func)
Set a callback to be called before fr_fault()
Definition debug.c:1211
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.
Definition debug.c:1309
void fr_fault_log_hex(uint8_t const *data, size_t data_len)
Print data as a hex block.
Definition debug.c:1239
int fr_set_dumpable_init(void)
Get the current maximum for core files.
Definition debug.c:670
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program, unsigned long fault_signals)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1071
void fr_panic_on_free(TALLOC_CTX *ctx)
Insert memory into the context of another talloc memory chunk which causes a panic when freed.
Definition debug.c:575
static void _fr_talloc_fault(char const *reason)
Callback executed on fatal talloc error.
Definition debug.c:951
static void _fr_talloc_log(char const *msg)
Wrapper to pass talloc log output to our fr_fault_log function.
Definition debug.c:963
static int fr_get_pr_dumpable_flag(void)
Get the processes dumpable flag.
Definition debug.c:658
fr_debug_state_t fr_debug_state
Whether we're attached to by a debugger.
Definition debug.c:74
int fr_get_debug_state(void)
Definition debug.c:490
void fr_fault_log(char const *msg,...)
Log output to the fr_fault_log_fd.
Definition debug.c:1225
fr_debug_state_t
Definition debug.h:41
@ DEBUGGER_STATE_NOT_ATTACHED
We can attach, so a debugger must not be.
Definition debug.h:45
@ DEBUGGER_STATE_UNKNOWN_NO_PTRACE
We don't have ptrace so can't check.
Definition debug.h:42
@ DEBUGGER_STATE_UNKNOWN_NO_PTRACE_CAP
CAP_SYS_PTRACE not set for the process.
Definition debug.h:43
@ DEBUGGER_STATE_UNKNOWN
Unknown, likely fr_get_debug_state() not called yet.
Definition debug.h:44
@ DEBUGGER_STATE_ATTACHED
We can't attach, it's likely a debugger is already tracing.
Definition debug.h:46
int(* fr_fault_cb_t)(int signum)
Optional callback passed to fr_fault_setup.
Definition debug.h:66
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:52
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:272
char const * __lsan_default_suppressions(void)
unsigned char uint8_t
static size_t used
int fr_unset_signal(int sig)
Uninstall a signal for a specific handler.
Definition misc.c:76
int fr_set_signal(int sig, sig_t func)
Sets a signal handler using sigaction if available, else signal.
Definition misc.c:47
int vdprintf(int fd, char const *format, va_list args)
Definition missing.c:214
#define is_truncated(_ret, _max)
Definition print.h:48
static char const * mib
Definition radict.c:59
static char const * program
Definition radiusd.c:79
PUBLIC int vsnprintf(char *string, size_t length, char *format, va_list args)
Definition snprintf.c:506
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
TALLOC_CTX * talloc_autofree_context_global(void)
Definition talloc.c:1056
void * talloc_null_ctx(void)
Retrieve the current talloc NULL ctx.
Definition talloc.c:50
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1366
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061