Coverage Report

Created: 2025-12-31 06:18

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