Typo
[freeradius.git] / src / lib / debug.c
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 /**
18  * @file debug.c
19  * @brief Various functions to aid in debugging
20  *
21  * @copyright 2013  The FreeRADIUS server project
22  * @copyright 2013  Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23  */
24 #include <assert.h>
25 #include <freeradius-devel/libradius.h>
26 #include <sys/stat.h>
27
28 #if defined(HAVE_MALLOPT) && defined(HAVE_MALLOC_H)
29 #  include <malloc.h>
30 #endif
31
32 /*
33  *      runtime backtrace functions are not POSIX but are included in
34  *      glibc, OSX >= 10.5 and various BSDs
35  */
36 #ifdef HAVE_EXECINFO
37 #  include <execinfo.h>
38 #endif
39
40 #ifdef HAVE_SYS_PRCTL_H
41 #  include <sys/prctl.h>
42 #endif
43
44 #ifdef HAVE_SYS_RESOURCE_H
45 #  include <sys/resource.h>
46 #endif
47
48 #ifdef HAVE_PTHREAD_H
49 #  define PTHREAD_MUTEX_LOCK pthread_mutex_lock
50 #  define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
51 #else
52 #  define PTHREAD_MUTEX_LOCK(_x)
53 #  define PTHREAD_MUTEX_UNLOCK(_x)
54 #endif
55
56 #ifdef HAVE_EXECINFO
57 #  define MAX_BT_FRAMES 128
58 #  define MAX_BT_CBUFF  65536                           //!< Should be a power of 2
59
60 #  ifdef HAVE_PTHREAD_H
61 static pthread_mutex_t fr_debug_init = PTHREAD_MUTEX_INITIALIZER;
62 #  endif
63
64 typedef struct fr_bt_info {
65         void            *obj;                           //!< Memory address of the block of allocated memory.
66         void            *frames[MAX_BT_FRAMES];         //!< Backtrace frame data
67         int             count;                          //!< Number of frames stored
68 } fr_bt_info_t;
69
70 struct fr_bt_marker {
71         void            *obj;                           //!< Pointer to the parent object, this is our needle
72                                                         //!< when we iterate over the contents of the circular buffer.
73         fr_cbuff_t      *cbuff;                         //!< Where we temporarily store the backtraces
74 };
75 #endif
76
77 static char panic_action[512];                          //!< The command to execute when panicking.
78 static fr_fault_cb_t panic_cb = NULL;                   //!< Callback to execute whilst panicking, before the
79                                                         //!< panic_action.
80 static fr_fault_log_t fr_fault_log = NULL;              //!< Function to use to process logging output.
81 static int fr_fault_log_fd = STDERR_FILENO;             //!< Where to write debug output.
82
83 static int fr_debugger_present = -1;                    //!< Whether were attached to by a debugger.
84
85 #ifdef HAVE_SYS_RESOURCE_H
86 static struct rlimit core_limits;
87 #endif
88
89 static TALLOC_CTX *talloc_null_ctx;
90 static TALLOC_CTX *talloc_autofree_ctx;
91
92 #define FR_FAULT_LOG(fmt, ...) fr_fault_log(fmt "\n", ## __VA_ARGS__)
93
94 /** Stub callback to see if the SIGTRAP handler is overriden
95  *
96  * @param signum signal raised.
97  */
98 static void _sigtrap_handler(UNUSED int signum)
99 {
100         fr_debugger_present = 0;
101         signal(SIGTRAP, SIG_DFL);
102 }
103
104 /** Break in debugger (if were running under a debugger)
105  *
106  * If the server is running under a debugger this will raise a
107  * SIGTRAP which will pause the running process.
108  *
109  * If the server is not running under debugger then this will do nothing.
110  */
111 void fr_debug_break(void)
112 {
113         if (fr_debugger_present == -1) {
114                 fr_debugger_present = 0;
115                 signal(SIGTRAP, _sigtrap_handler);
116                 raise(SIGTRAP);
117         } else if (fr_debugger_present == 1) {
118                 raise(SIGTRAP);
119         }
120 }
121
122 #ifdef HAVE_EXECINFO
123 /** Print backtrace entry for a given object
124  *
125  * @param cbuff to search in.
126  * @param obj pointer to original object
127  */
128 void backtrace_print(fr_cbuff_t *cbuff, void *obj)
129 {
130         fr_bt_info_t *p;
131         bool found = false;
132
133         while ((p = fr_cbuff_rp_next(cbuff, NULL))) {
134                 if ((p->obj == obj) || !obj) {
135                         found = true;
136
137                         fprintf(stderr, "Stacktrace for: %p\n", p->obj);
138                         backtrace_symbols_fd(p->frames, p->count, STDERR_FILENO);
139                 }
140         };
141
142         if (!found) {
143                 fprintf(stderr, "No backtrace available for %p", obj);
144         }
145 }
146
147 /** Generate a backtrace for an object
148  *
149  * If this is the first entry being inserted
150  */
151 int fr_backtrace_do(fr_bt_marker_t *marker)
152 {
153         fr_bt_info_t *bt;
154
155         if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) return -1;
156
157         bt = talloc_zero(NULL, fr_bt_info_t);
158         if (!bt) return -1;
159
160         bt->obj = marker->obj;
161         bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
162
163         fr_cbuff_rp_insert(marker->cbuff, bt);
164
165         return 0;
166 }
167
168 /** Inserts a backtrace marker into the provided context
169  *
170  * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
171  *
172  * Code augmentation should look something like:
173 @verbatim
174         // Create a static cbuffer pointer, the first call to backtrace_attach will initialise it
175         static fr_cbuff_t *my_obj_bt;
176
177         my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
178                 my_obj_t *this;
179
180                 this = talloc(ctx, my_obj_t);
181
182                 // Attach backtrace marker to object
183                 backtrace_attach(&my_obj_bt, this);
184
185                 return this;
186         }
187 @endverbatim
188  *
189  * Then, later when a double free occurs:
190 @verbatim
191         (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
192 @endverbatim
193  *
194  * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
195  * values, but should at least show the code path taken.
196  *
197  * @param cbuff this should be a pointer to a static *fr_cbuff.
198  * @param obj we want to generate a backtrace for.
199  */
200 fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj)
201 {
202         fr_bt_marker_t *marker;
203
204         if (*cbuff == NULL) {
205                 PTHREAD_MUTEX_LOCK(&fr_debug_init);
206                 /* Check again now we hold the mutex - eww*/
207                 if (*cbuff == NULL) *cbuff = fr_cbuff_alloc(NULL, MAX_BT_CBUFF, true);
208                 PTHREAD_MUTEX_UNLOCK(&fr_debug_init);
209         }
210
211         marker = talloc(obj, fr_bt_marker_t);
212         if (!marker) {
213                 return NULL;
214         }
215
216         marker->obj = (void *) obj;
217         marker->cbuff = *cbuff;
218
219         fprintf(stderr, "Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
220         /*
221          *      Generate the backtrace for memory allocation
222          */
223         fr_backtrace_do(marker);
224         talloc_set_destructor(marker, fr_backtrace_do);
225
226         return marker;
227 }
228 #else
229 void backtrace_print(UNUSED fr_cbuff_t *cbuff, UNUSED void *obj)
230 {
231         fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
232 }
233 fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_cbuff_t **cbuff, UNUSED TALLOC_CTX *obj)
234 {
235         fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
236         abort();
237 }
238 #endif /* ifdef HAVE_EXECINFO */
239
240 static int _panic_on_free(UNUSED char *foo)
241 {
242         fr_fault(SIGUSR1);
243         return -1;      /* this should make the free fail */
244 }
245
246 /** Insert memory into the context of another talloc memory chunk which
247  * causes a panic when freed.
248  *
249  * @param ctx TALLOC_CTX to monitor for frees.
250  */
251 void fr_panic_on_free(TALLOC_CTX *ctx)
252 {
253         char *ptr;
254
255         ptr = talloc(ctx, char);
256         talloc_set_destructor(ptr, _panic_on_free);
257 }
258
259 /** Set the dumpable flag, also controls whether processes can PATTACH
260  *
261  * @param dumpable whether we should allow core dumping
262  */
263 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
264 static int fr_set_dumpable_flag(bool dumpable)
265 {
266         if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
267                 fr_strerror_printf("Cannot re-enable core dumps: prctl(PR_SET_DUMPABLE) failed: %s",
268                                    fr_syserror(errno));
269                 return -1;
270         }
271
272         return 0;
273 }
274 #else
275 static int fr_set_dumpable_flag(UNUSED bool dumpable)
276 {
277         fr_strerror_printf("Changing value of PR_DUMPABLE not supported on this system");
278         return -2;
279 }
280 #endif
281
282 /** Get the processes dumpable flag
283  *
284  */
285 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE)
286 static int fr_get_dumpable_flag(void)
287 {
288         int ret;
289
290         ret = prctl(PR_GET_DUMPABLE);
291         if (ret < 0) {
292                 fr_strerror_printf("Cannot get dumpable flag: %s", fr_syserror(errno));
293                 return -1;
294         }
295
296         /*
297          *  Linux is crazy and prctl sometimes returns 2 for disabled
298          */
299         if (ret != 1) return 0;
300         return 1;
301 }
302 #else
303 static int fr_get_dumpable_flag(void)
304 {
305         fr_strerror_printf("Getting value of PR_DUMPABLE not supported on this system");
306         return -2;
307 }
308 #endif
309
310
311 /** Get the current maximum for core files
312  *
313  * Do this before anything else so as to ensure it's properly initialized.
314  */
315 int fr_set_dumpable_init(void)
316 {
317 #ifdef HAVE_SYS_RESOURCE_H
318         if (getrlimit(RLIMIT_CORE, &core_limits) < 0) {
319                 fr_strerror_printf("Failed to get current core limit:  %s", fr_syserror(errno));
320                 return -1;
321         }
322 #endif
323         return 0;
324 }
325
326 /** Enable or disable core dumps
327  *
328  * @param allow_core_dumps whether to enable or disable core dumps.
329  */
330 int fr_set_dumpable(bool allow_core_dumps)
331 {
332         /*
333          *      If configured, turn core dumps off.
334          */
335         if (!allow_core_dumps) {
336 #ifdef HAVE_SYS_RESOURCE_H
337                 struct rlimit no_core;
338
339                 no_core.rlim_cur = 0;
340                 no_core.rlim_max = 0;
341
342                 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
343                         fr_strerror_printf("Failed disabling core dumps: %s", fr_syserror(errno));
344
345                         return -1;
346                 }
347 #endif
348                 return 0;
349         }
350
351         if (fr_set_dumpable_flag(true) < 0) return -1;
352
353         /*
354          *      Reset the core dump limits to their original value.
355          */
356 #ifdef HAVE_SYS_RESOURCE_H
357         if (setrlimit(RLIMIT_CORE, &core_limits) < 0) {
358                 fr_strerror_printf("Cannot update core dump limit: %s", fr_syserror(errno));
359
360                 return -1;
361         }
362 #endif
363         return 0;
364 }
365
366 /** Check to see if panic_action file is world writeable
367  *
368  * @return 0 if file is OK, else -1.
369  */
370 static int fr_fault_check_permissions(void)
371 {
372         char const *p, *q;
373         size_t len;
374         char filename[256];
375         struct stat statbuf;
376
377         /*
378          *      Try and guess which part of the command is the binary, and check to see if
379          *      it's world writeable, to try and save the admin from their own stupidity.
380          *
381          *      @fixme we should do this properly and take into account single and double
382          *      quotes.
383          */
384         if ((q = strchr(panic_action, ' '))) {
385                 /*
386                  *      need to use a static buffer, because mallocing memory in a signal handler
387                  *      is a bad idea and can result in deadlock.
388                  */
389                 len = snprintf(filename, sizeof(filename), "%.*s", (int)(q - panic_action), panic_action);
390                 if (is_truncated(len, sizeof(filename))) {
391                         fr_strerror_printf("Failed writing panic_action to temporary buffer (truncated)");
392                         return -1;
393                 }
394                 p = filename;
395         } else {
396                 p = panic_action;
397         }
398
399         if (stat(p, &statbuf) == 0) {
400 #ifdef S_IWOTH
401                 if ((statbuf.st_mode & S_IWOTH) != 0) {
402                         fr_strerror_printf("panic_action file \"%s\" is globally writable", p);
403                         return -1;
404                 }
405 #endif
406         }
407
408         return 0;
409 }
410
411 /** Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
412  *
413  * @param sig caught
414  */
415 void fr_fault(int sig)
416 {
417         char cmd[sizeof(panic_action) + 20];
418         char *out = cmd;
419         size_t left = sizeof(cmd), ret;
420
421         char const *p = panic_action;
422         char const *q;
423
424         int code;
425
426         /*
427          *      Makes the backtraces slightly cleaner
428          */
429         memset(cmd, 0, sizeof(cmd));
430
431         FR_FAULT_LOG("CAUGHT SIGNAL: %s", strsignal(sig));
432
433         /*
434          *      Check for administrator sanity.
435          */
436         if (fr_fault_check_permissions() < 0) {
437                 FR_FAULT_LOG("Refusing to execute panic action: %s", fr_strerror());
438                 goto finish;
439         }
440
441         /*
442          *      Run the callback if one was registered
443          */
444         if (panic_cb && (panic_cb(sig) < 0)) goto finish;
445
446         /*
447          *      Produce a simple backtrace - They've very basic but at least give us an
448          *      idea of the area of the code we hit the issue in.
449          *
450          *      See below in fr_fault_setup() and
451          *      https://sourceware.org/bugzilla/show_bug.cgi?id=16159
452          *      for why we only print backtraces in debug builds if we're using GLIBC.
453          */
454 #if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
455         {
456                 size_t frame_count, i;
457                 void *stack[MAX_BT_FRAMES];
458                 char **strings;
459
460                 frame_count = backtrace(stack, MAX_BT_FRAMES);
461
462                 FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
463
464                 /*
465                  *      Only use backtrace_symbols() if we don't have a logging fd.
466                  *      If the server has experienced memory corruption, there's
467                  *      a high probability that calling backtrace_symbols() which
468                  *      mallocs more memory, will fail.
469                  */
470                 if (fr_fault_log_fd < 0) {
471                         strings = backtrace_symbols(stack, frame_count);
472                         for (i = 0; i < frame_count; i++) {
473                                 FR_FAULT_LOG("%s", strings[i]);
474                         }
475                         free(strings);
476                 } else {
477                         backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
478                 }
479         }
480 #endif
481
482         /* No panic action set... */
483         if (panic_action[0] == '\0') {
484                 FR_FAULT_LOG("No panic action set");
485                 goto finish;
486         }
487
488         /* Substitute %p for the current PID (useful for attaching a debugger) */
489         while ((q = strstr(p, "%p"))) {
490                 out += ret = snprintf(out, left, "%.*s%d", (int) (q - p), p, (int) getpid());
491                 if (left <= ret) {
492                 oob:
493                         FR_FAULT_LOG("Panic action too long");
494                         fr_exit_now(1);
495                 }
496                 left -= ret;
497                 p = q + 2;
498         }
499         if (strlen(p) >= left) goto oob;
500         strlcpy(out, p, left);
501
502         FR_FAULT_LOG("Calling: %s", cmd);
503
504         {
505                 bool disable = false;
506
507                 /*
508                  *      Here we temporarily enable the dumpable flag so if GBD or LLDB
509                  *      is called in the panic_action, they can pattach tot he running
510                  *      process.
511                  */
512                 if (fr_get_dumpable_flag() == 0) {
513                         if ((fr_set_dumpable_flag(true) < 0) || !fr_get_dumpable_flag()) {
514                                 FR_FAULT_LOG("Failed setting dumpable flag, pattach may not work: %s", fr_strerror());
515                         } else {
516                                 disable = true;
517                         }
518                         FR_FAULT_LOG("Temporarily setting PR_DUMPABLE to 1");
519                 }
520
521                 code = system(cmd);
522
523                 /*
524                  *      We only want to error out here, if dumpable was originally disabled
525                  *      and we managed to change the value to enabled, but failed
526                  *      setting it back to disabled.
527                  */
528                 if (disable) {
529                         FR_FAULT_LOG("Resetting PR_DUMPABLE to 0");
530                         if (fr_set_dumpable_flag(false) < 0) {
531                                 FR_FAULT_LOG("Failed reseting dumpable flag to off: %s", fr_strerror());
532                                 FR_FAULT_LOG("Exiting due to insecure process state");
533                                 fr_exit_now(1);
534                         }
535                 }
536         }
537
538         FR_FAULT_LOG("Panic action exited with %i", code);
539
540 finish:
541 #ifdef SIGUSR1
542         if (sig == SIGUSR1) {
543                 return;
544         }
545 #endif
546         fr_exit_now(1);
547 }
548
549 #ifdef SIGABRT
550 /** Work around debuggers which can't backtrace past the signal handler
551  *
552  * At least this provides us some information when we get talloc errors.
553  */
554 static void _fr_talloc_fault(char const *reason)
555 {
556         fr_fault_log("talloc abort: %s\n", reason);
557         fr_fault(SIGABRT);
558 }
559 #endif
560
561 /** Wrapper to pass talloc log output to our fr_fault_log function
562  *
563  */
564 static void _fr_talloc_log(char const *msg)
565 {
566         fr_fault_log("%s\n", msg);
567 }
568
569 /** Generate a talloc memory report for a context and print to stderr/stdout
570  *
571  * @param ctx to generate a report for, may be NULL in which case the root context is used.
572  */
573 int fr_log_talloc_report(TALLOC_CTX *ctx)
574 {
575         FILE *log;
576         int i = 0;
577         int fd;
578
579         fd = dup(fr_fault_log_fd);
580         if (fd < 0) {
581                 fr_strerror_printf("Couldn't write memory report, failed to dup log fd: %s", fr_syserror(errno));
582                 return -1;
583         }
584         log = fdopen(fd, "w");
585         if (!log) {
586                 close(fd);
587                 fr_strerror_printf("Couldn't write memory report, fdopen failed: %s", fr_syserror(errno));
588                 return -1;
589         }
590
591         if (!ctx) {
592                 fprintf(log, "Current state of talloced memory:\n");
593                 talloc_report_full(talloc_null_ctx, log);
594         } else {
595                 fprintf(log, "Talloc chunk lineage:\n");
596                 fprintf(log, "%p (%s)", ctx, talloc_get_name(ctx));
597                 while ((ctx = talloc_parent(ctx))) fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
598                 fprintf(log, "\n");
599
600                 do {
601                         fprintf(log, "Talloc context level %i:\n", i++);
602                         talloc_report_full(ctx, log);
603                 } while ((ctx = talloc_parent(ctx)) &&
604                          (talloc_parent(ctx) != talloc_autofree_ctx) && /* Stop before we hit the autofree ctx */
605                          (talloc_parent(ctx) != talloc_null_ctx));      /* Stop before we hit NULL ctx */
606         }
607
608         fclose(log);
609
610         return 0;
611 }
612
613 /** Signal handler to print out a talloc memory report
614  *
615  * @param sig caught
616  */
617 static void _fr_fault_mem_report(int sig)
618 {
619         fr_fault_log("CAUGHT SIGNAL: %s\n", strsignal(sig));
620
621         if (fr_log_talloc_report(NULL) < 0) fr_perror("memreport");
622 }
623
624 static int _fr_disable_null_tracking(UNUSED bool *p)
625 {
626         talloc_disable_null_tracking();
627         return 0;
628 }
629
630 /** Registers signal handlers to execute panic_action on fatal signal
631  *
632  * May be called multiple time to change the panic_action/program.
633  *
634  * @param cmd to execute on fault. If present %p will be substituted
635  *        for the parent PID before the command is executed, and %e
636  *        will be substituted for the currently running program.
637  * @param program Name of program currently executing (argv[0]).
638  * @return 0 on success -1 on failure.
639  */
640 int fr_fault_setup(char const *cmd, char const *program)
641 {
642         static bool setup = false;
643
644         char *out = panic_action;
645         size_t left = sizeof(panic_action), ret;
646
647         char const *p = cmd;
648         char const *q;
649
650         if (cmd) {
651                 /* Substitute %e for the current program */
652                 while ((q = strstr(p, "%e"))) {
653                         out += ret = snprintf(out, left, "%.*s%s", (int) (q - p), p, program ? program : "");
654                         if (left <= ret) {
655                         oob:
656                                 fr_strerror_printf("Panic action too long");
657                                 return -1;
658                         }
659                         left -= ret;
660                         p = q + 2;
661                 }
662                 if (strlen(p) >= left) goto oob;
663                 strlcpy(out, p, left);
664         } else {
665                 *panic_action = '\0';
666         }
667
668         /*
669          *      Check for administrator sanity.
670          */
671         if (fr_fault_check_permissions() < 0) return -1;
672
673         /* Unsure what the side effects of changing the signal handler mid execution might be */
674         if (!setup) {
675 #ifdef SIGSEGV
676                 if (fr_set_signal(SIGSEGV, fr_fault) < 0) return -1;
677 #endif
678 #ifdef SIGBUS
679                 if (fr_set_signal(SIGBUS, fr_fault) < 0) return -1;
680 #endif
681 #ifdef SIGABRT
682                 if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1;
683                 /*
684                  *  Use this instead of abort so we get a
685                  *  full backtrace with broken versions of LLDB
686                  */
687                 talloc_set_abort_fn(_fr_talloc_fault);
688 #endif
689 #ifdef SIGFPE
690                 if (fr_set_signal(SIGFPE, fr_fault) < 0) return -1;
691 #endif
692
693 #ifdef SIGUSR1
694                 if (fr_set_signal(SIGUSR1, fr_fault) < 0) return -1;
695 #endif
696
697 #ifdef SIGUSR2
698                 if (fr_set_signal(SIGUSR2, _fr_fault_mem_report) < 0) return -1;
699 #endif
700
701                 /*
702                  *  Setup the default logger
703                  */
704                 if (!fr_fault_log) fr_fault_set_log_fn(NULL);
705                 talloc_set_log_fn(_fr_talloc_log);
706
707                 /*
708                  *  Needed for memory reports
709                  */
710                 {
711                         TALLOC_CTX *tmp;
712                         bool *marker;
713
714                         tmp = talloc(NULL, bool);
715                         talloc_null_ctx = talloc_parent(tmp);
716                         talloc_free(tmp);
717
718                         /*
719                          *  Disable null tracking on exit, else valgrind complains
720                          */
721                         talloc_autofree_ctx = talloc_autofree_context();
722                         marker = talloc(talloc_autofree_ctx, bool);
723                         talloc_set_destructor(marker, _fr_disable_null_tracking);
724                 }
725
726 #if defined(HAVE_MALLOPT) && !defined(NDEBUG)
727                 /*
728                  *  If were using glibc malloc > 2.4 this scribbles over
729                  *  uninitialised and freed memory, to make memory issues easier
730                  *  to track down.
731                  */
732                 if (!getenv("TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
733                 mallopt(M_CHECK_ACTION, 3);
734 #endif
735
736 #if defined(HAVE_EXECINFO) && defined(__GNUC__) && !defined(NDEBUG)
737                /*
738                 *  We need to pre-load lgcc_s, else we can get into a deadlock
739                 *  in fr_fault, as backtrace() attempts to dlopen it.
740                 *
741                 *  Apparently there's a performance impact of loading lgcc_s,
742                 *  so only do it if this is a debug build.
743                 *
744                 *  See: https://sourceware.org/bugzilla/show_bug.cgi?id=16159
745                 */
746                 {
747                         void *stack[10];
748
749                         backtrace(stack, 10);
750                 }
751 #endif
752         }
753         setup = true;
754
755         return 0;
756 }
757
758 /** Set a callback to be called before fr_fault()
759  *
760  * @param func to execute. If callback returns < 0
761  *      fr_fault will exit before running panic_action code.
762  */
763 void fr_fault_set_cb(fr_fault_cb_t func)
764 {
765         panic_cb = func;
766 };
767
768 /** Default logger, logs output to stderr
769  *
770  */
771 static void CC_HINT(format (printf, 1, 2)) _fr_fault_log(char const *msg, ...)
772 {
773         va_list ap;
774
775         va_start(ap, msg);
776         vfprintf(stderr, msg, ap);
777         va_end(ap);
778 }
779
780
781 /** Set a file descriptor to log panic_action output to.
782  *
783  * @param func to call to output log messages.
784  */
785 void fr_fault_set_log_fn(fr_fault_log_t func)
786 {
787         fr_fault_log = func ? func : _fr_fault_log;
788 }
789
790 /** Set a file descriptor to log memory reports to.
791  *
792  * @param fd to write output to.
793  */
794 void fr_fault_set_log_fd(int fd)
795 {
796         fr_fault_log_fd = fd;
797 }
798
799
800 #ifdef WITH_VERIFY_PTR
801
802 /*
803  *      Verify a VALUE_PAIR
804  */
805 inline void fr_verify_vp(char const *file, int line, VALUE_PAIR const *vp)
806 {
807         if (!vp) {
808                 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR pointer was NULL", file, line);
809                 fr_assert(0);
810                 fr_exit_now(0);
811         }
812
813         (void) talloc_get_type_abort(vp, VALUE_PAIR);
814
815         if (vp->data.ptr) switch (vp->da->type) {
816         case PW_TYPE_OCTETS:
817         case PW_TYPE_TLV:
818         {
819                 size_t len;
820                 TALLOC_CTX *parent;
821
822                 if (!talloc_get_type(vp->data.ptr, uint8_t)) {
823                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
824                                 "uint8_t but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
825                         (void) talloc_get_type_abort(vp->data.ptr, uint8_t);
826                 }
827
828                 len = talloc_array_length(vp->vp_octets);
829                 if (vp->length > len) {
830                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
831                                 "uint8_t data buffer length %zu\n", file, line, vp->da->name, vp->length, len);
832                         fr_assert(0);
833                         fr_exit_now(1);
834                 }
835
836                 parent = talloc_parent(vp->data.ptr);
837                 if (parent != vp) {
838                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
839                                 "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
840                                 file, line, vp->da->name,
841                                 vp, parent, parent ? talloc_get_name(parent) : "NULL");
842                         fr_assert(0);
843                         fr_exit_now(1);
844                 }
845         }
846                 break;
847
848         case PW_TYPE_STRING:
849         {
850                 size_t len;
851                 TALLOC_CTX *parent;
852
853                 if (!talloc_get_type(vp->data.ptr, char)) {
854                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
855                                 "char but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
856                         (void) talloc_get_type_abort(vp->data.ptr, char);
857                 }
858
859                 len = (talloc_array_length(vp->vp_strvalue) - 1);
860                 if (vp->length > len) {
861                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
862                                 "char buffer length %zu\n", file, line, vp->da->name, vp->length, len);
863                         fr_assert(0);
864                         fr_exit_now(1);
865                 }
866
867                 if (vp->vp_strvalue[vp->length] != '\0') {
868                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer not \\0 "
869                                 "terminated\n", file, line, vp->da->name);
870                         fr_assert(0);
871                         fr_exit_now(1);
872                 }
873
874                 parent = talloc_parent(vp->data.ptr);
875                 if (parent != vp) {
876                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" uint8_t buffer is not "
877                                 "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
878                                 file, line, vp->da->name,
879                                 vp, parent, parent ? talloc_get_name(parent) : "NULL");
880                         fr_assert(0);
881                         fr_exit_now(1);
882                 }
883         }
884                 break;
885
886         default:
887                 break;
888         }
889 }
890
891 /*
892  *      Verify a pair list
893  */
894 void fr_verify_list(char const *file, int line, TALLOC_CTX *expected, VALUE_PAIR *vps)
895 {
896         vp_cursor_t cursor;
897         VALUE_PAIR *vp;
898         TALLOC_CTX *parent;
899
900         for (vp = fr_cursor_init(&cursor, &vps);
901              vp;
902              vp = fr_cursor_next(&cursor)) {
903                 VERIFY_VP(vp);
904
905                 parent = talloc_parent(vp);
906                 if (expected && (parent != expected)) {
907                         fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: Expected VALUE_PAIR \"%s\" to be parented "
908                                 "by %p (%s), instead parented by %p (%s)\n",
909                                 file, line, vp->da->name,
910                                 expected, talloc_get_name(expected),
911                                 parent, parent ? talloc_get_name(parent) : "NULL");
912
913                         fr_log_talloc_report(expected);
914                         if (parent) fr_log_talloc_report(parent);
915
916                         assert(0);
917                 }
918
919         }
920 }
921 #endif