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