xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp (revision d89ec533011f513df1010f142a111086a0785f09)
13cab2bb3Spatrick //===-- sanitizer_termination.cpp -------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick ///
93cab2bb3Spatrick /// This file contains the Sanitizer termination functions CheckFailed and Die,
103cab2bb3Spatrick /// and the callback functionalities associated with them.
113cab2bb3Spatrick ///
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick 
143cab2bb3Spatrick #include "sanitizer_common.h"
153cab2bb3Spatrick #include "sanitizer_libc.h"
163cab2bb3Spatrick 
173cab2bb3Spatrick namespace __sanitizer {
183cab2bb3Spatrick 
193cab2bb3Spatrick static const int kMaxNumOfInternalDieCallbacks = 5;
203cab2bb3Spatrick static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
213cab2bb3Spatrick 
AddDieCallback(DieCallbackType callback)223cab2bb3Spatrick bool AddDieCallback(DieCallbackType callback) {
233cab2bb3Spatrick   for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
243cab2bb3Spatrick     if (InternalDieCallbacks[i] == nullptr) {
253cab2bb3Spatrick       InternalDieCallbacks[i] = callback;
263cab2bb3Spatrick       return true;
273cab2bb3Spatrick     }
283cab2bb3Spatrick   }
293cab2bb3Spatrick   return false;
303cab2bb3Spatrick }
313cab2bb3Spatrick 
RemoveDieCallback(DieCallbackType callback)323cab2bb3Spatrick bool RemoveDieCallback(DieCallbackType callback) {
333cab2bb3Spatrick   for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
343cab2bb3Spatrick     if (InternalDieCallbacks[i] == callback) {
353cab2bb3Spatrick       internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
363cab2bb3Spatrick                        sizeof(InternalDieCallbacks[0]) *
373cab2bb3Spatrick                            (kMaxNumOfInternalDieCallbacks - i - 1));
383cab2bb3Spatrick       InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
393cab2bb3Spatrick       return true;
403cab2bb3Spatrick     }
413cab2bb3Spatrick   }
423cab2bb3Spatrick   return false;
433cab2bb3Spatrick }
443cab2bb3Spatrick 
453cab2bb3Spatrick static DieCallbackType UserDieCallback;
SetUserDieCallback(DieCallbackType callback)463cab2bb3Spatrick void SetUserDieCallback(DieCallbackType callback) {
473cab2bb3Spatrick   UserDieCallback = callback;
483cab2bb3Spatrick }
493cab2bb3Spatrick 
Die()503cab2bb3Spatrick void NORETURN Die() {
513cab2bb3Spatrick   if (UserDieCallback)
523cab2bb3Spatrick     UserDieCallback();
533cab2bb3Spatrick   for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
543cab2bb3Spatrick     if (InternalDieCallbacks[i])
553cab2bb3Spatrick       InternalDieCallbacks[i]();
563cab2bb3Spatrick   }
573cab2bb3Spatrick   if (common_flags()->abort_on_error)
583cab2bb3Spatrick     Abort();
593cab2bb3Spatrick   internal__exit(common_flags()->exitcode);
603cab2bb3Spatrick }
613cab2bb3Spatrick 
62*d89ec533Spatrick static void (*CheckUnwindCallback)();
SetCheckUnwindCallback(void (* callback)())63*d89ec533Spatrick void SetCheckUnwindCallback(void (*callback)()) {
64*d89ec533Spatrick   CheckUnwindCallback = callback;
653cab2bb3Spatrick }
663cab2bb3Spatrick 
CheckFailed(const char * file,int line,const char * cond,u64 v1,u64 v2)673cab2bb3Spatrick void NORETURN CheckFailed(const char *file, int line, const char *cond,
683cab2bb3Spatrick                           u64 v1, u64 v2) {
69*d89ec533Spatrick   u32 tid = GetTid();
70*d89ec533Spatrick   Printf("%s: CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx) (tid=%u)\n",
71*d89ec533Spatrick          SanitizerToolName, StripModuleName(file), line, cond, (uptr)v1,
72*d89ec533Spatrick          (uptr)v2, tid);
73*d89ec533Spatrick   static atomic_uint32_t first_tid;
74*d89ec533Spatrick   u32 cmp = 0;
75*d89ec533Spatrick   if (!atomic_compare_exchange_strong(&first_tid, &cmp, tid,
76*d89ec533Spatrick                                       memory_order_relaxed)) {
77*d89ec533Spatrick     if (cmp == tid) {
78*d89ec533Spatrick       // Recursing into CheckFailed.
79*d89ec533Spatrick     } else {
80*d89ec533Spatrick       // Another thread fails already, let it print the stack and terminate.
81*d89ec533Spatrick       SleepForSeconds(2);
82*d89ec533Spatrick     }
833cab2bb3Spatrick     Trap();
843cab2bb3Spatrick   }
85*d89ec533Spatrick   if (CheckUnwindCallback)
86*d89ec533Spatrick     CheckUnwindCallback();
873cab2bb3Spatrick   Die();
883cab2bb3Spatrick }
893cab2bb3Spatrick 
903cab2bb3Spatrick } // namespace __sanitizer
913cab2bb3Spatrick 
923cab2bb3Spatrick using namespace __sanitizer;
933cab2bb3Spatrick 
943cab2bb3Spatrick extern "C" {
953cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_set_death_callback(void (* callback)(void))963cab2bb3Spatrick void __sanitizer_set_death_callback(void (*callback)(void)) {
973cab2bb3Spatrick   SetUserDieCallback(callback);
983cab2bb3Spatrick }
993cab2bb3Spatrick }  // extern "C"
100