xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1 //===-- sanitizer_termination.cpp -------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// This file contains the Sanitizer termination functions CheckFailed and Die,
10 /// and the callback functionalities associated with them.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "sanitizer_common.h"
15 #include "sanitizer_libc.h"
16 
17 namespace __sanitizer {
18 
19 static const int kMaxNumOfInternalDieCallbacks = 5;
20 static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
21 
AddDieCallback(DieCallbackType callback)22 bool AddDieCallback(DieCallbackType callback) {
23   for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
24     if (InternalDieCallbacks[i] == nullptr) {
25       InternalDieCallbacks[i] = callback;
26       return true;
27     }
28   }
29   return false;
30 }
31 
RemoveDieCallback(DieCallbackType callback)32 bool RemoveDieCallback(DieCallbackType callback) {
33   for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
34     if (InternalDieCallbacks[i] == callback) {
35       internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
36                        sizeof(InternalDieCallbacks[0]) *
37                            (kMaxNumOfInternalDieCallbacks - i - 1));
38       InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
39       return true;
40     }
41   }
42   return false;
43 }
44 
45 static DieCallbackType UserDieCallback;
SetUserDieCallback(DieCallbackType callback)46 void SetUserDieCallback(DieCallbackType callback) {
47   UserDieCallback = callback;
48 }
49 
Die()50 void NORETURN Die() {
51   if (UserDieCallback)
52     UserDieCallback();
53   for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
54     if (InternalDieCallbacks[i])
55       InternalDieCallbacks[i]();
56   }
57   if (common_flags()->abort_on_error)
58     Abort();
59   internal__exit(common_flags()->exitcode);
60 }
61 
62 static void (*CheckUnwindCallback)();
SetCheckUnwindCallback(void (* callback)())63 void SetCheckUnwindCallback(void (*callback)()) {
64   CheckUnwindCallback = callback;
65 }
66 
CheckFailed(const char * file,int line,const char * cond,u64 v1,u64 v2)67 void NORETURN CheckFailed(const char *file, int line, const char *cond,
68                           u64 v1, u64 v2) {
69   u32 tid = GetTid();
70   Printf("%s: CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx) (tid=%u)\n",
71          SanitizerToolName, StripModuleName(file), line, cond, (uptr)v1,
72          (uptr)v2, tid);
73   static atomic_uint32_t first_tid;
74   u32 cmp = 0;
75   if (!atomic_compare_exchange_strong(&first_tid, &cmp, tid,
76                                       memory_order_relaxed)) {
77     if (cmp == tid) {
78       // Recursing into CheckFailed.
79     } else {
80       // Another thread fails already, let it print the stack and terminate.
81       SleepForSeconds(2);
82     }
83     Trap();
84   }
85   if (CheckUnwindCallback)
86     CheckUnwindCallback();
87   Die();
88 }
89 
90 } // namespace __sanitizer
91 
92 using namespace __sanitizer;
93 
94 extern "C" {
95 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_set_death_callback(void (* callback)(void))96 void __sanitizer_set_death_callback(void (*callback)(void)) {
97   SetUserDieCallback(callback);
98 }
99 }  // extern "C"
100