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