1*0fca6ea1SDimitry Andric //===--- rtsan_context.cpp - Realtime Sanitizer -----------------*- C++ -*-===// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric // 9*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 10*0fca6ea1SDimitry Andric 11*0fca6ea1SDimitry Andric #include <rtsan/rtsan_context.h> 12*0fca6ea1SDimitry Andric 13*0fca6ea1SDimitry Andric #include <rtsan/rtsan_stack.h> 14*0fca6ea1SDimitry Andric 15*0fca6ea1SDimitry Andric #include <sanitizer_common/sanitizer_allocator_internal.h> 16*0fca6ea1SDimitry Andric #include <sanitizer_common/sanitizer_stacktrace.h> 17*0fca6ea1SDimitry Andric 18*0fca6ea1SDimitry Andric #include <new> 19*0fca6ea1SDimitry Andric #include <pthread.h> 20*0fca6ea1SDimitry Andric #include <stdio.h> 21*0fca6ea1SDimitry Andric #include <stdlib.h> 22*0fca6ea1SDimitry Andric 23*0fca6ea1SDimitry Andric static pthread_key_t context_key; 24*0fca6ea1SDimitry Andric static pthread_once_t key_once = PTHREAD_ONCE_INIT; 25*0fca6ea1SDimitry Andric 26*0fca6ea1SDimitry Andric // InternalFree cannot be passed directly to pthread_key_create 27*0fca6ea1SDimitry Andric // because it expects a signature with only one arg 28*0fca6ea1SDimitry Andric static void InternalFreeWrapper(void *ptr) { __sanitizer::InternalFree(ptr); } 29*0fca6ea1SDimitry Andric 30*0fca6ea1SDimitry Andric static __rtsan::Context &GetContextForThisThreadImpl() { 31*0fca6ea1SDimitry Andric auto make_thread_local_context_key = []() { 32*0fca6ea1SDimitry Andric CHECK_EQ(pthread_key_create(&context_key, InternalFreeWrapper), 0); 33*0fca6ea1SDimitry Andric }; 34*0fca6ea1SDimitry Andric 35*0fca6ea1SDimitry Andric pthread_once(&key_once, make_thread_local_context_key); 36*0fca6ea1SDimitry Andric __rtsan::Context *current_thread_context = 37*0fca6ea1SDimitry Andric static_cast<__rtsan::Context *>(pthread_getspecific(context_key)); 38*0fca6ea1SDimitry Andric if (current_thread_context == nullptr) { 39*0fca6ea1SDimitry Andric current_thread_context = static_cast<__rtsan::Context *>( 40*0fca6ea1SDimitry Andric __sanitizer::InternalAlloc(sizeof(__rtsan::Context))); 41*0fca6ea1SDimitry Andric new (current_thread_context) __rtsan::Context(); 42*0fca6ea1SDimitry Andric pthread_setspecific(context_key, current_thread_context); 43*0fca6ea1SDimitry Andric } 44*0fca6ea1SDimitry Andric 45*0fca6ea1SDimitry Andric return *current_thread_context; 46*0fca6ea1SDimitry Andric } 47*0fca6ea1SDimitry Andric 48*0fca6ea1SDimitry Andric /* 49*0fca6ea1SDimitry Andric This is a placeholder stub for a future feature that will allow 50*0fca6ea1SDimitry Andric a user to configure RTSan's behaviour when a real-time safety 51*0fca6ea1SDimitry Andric violation is detected. The RTSan developers intend for the 52*0fca6ea1SDimitry Andric following choices to be made available, via a RTSAN_OPTIONS 53*0fca6ea1SDimitry Andric environment variable, in a future PR: 54*0fca6ea1SDimitry Andric 55*0fca6ea1SDimitry Andric i) exit, 56*0fca6ea1SDimitry Andric ii) continue, or 57*0fca6ea1SDimitry Andric iii) wait for user input from stdin. 58*0fca6ea1SDimitry Andric 59*0fca6ea1SDimitry Andric Until then, and to keep the first PRs small, only the exit mode 60*0fca6ea1SDimitry Andric is available. 61*0fca6ea1SDimitry Andric */ 62*0fca6ea1SDimitry Andric static void InvokeViolationDetectedAction() { exit(EXIT_FAILURE); } 63*0fca6ea1SDimitry Andric 64*0fca6ea1SDimitry Andric __rtsan::Context::Context() = default; 65*0fca6ea1SDimitry Andric 66*0fca6ea1SDimitry Andric void __rtsan::Context::RealtimePush() { realtime_depth++; } 67*0fca6ea1SDimitry Andric 68*0fca6ea1SDimitry Andric void __rtsan::Context::RealtimePop() { realtime_depth--; } 69*0fca6ea1SDimitry Andric 70*0fca6ea1SDimitry Andric void __rtsan::Context::BypassPush() { bypass_depth++; } 71*0fca6ea1SDimitry Andric 72*0fca6ea1SDimitry Andric void __rtsan::Context::BypassPop() { bypass_depth--; } 73*0fca6ea1SDimitry Andric 74*0fca6ea1SDimitry Andric void __rtsan::Context::ExpectNotRealtime( 75*0fca6ea1SDimitry Andric const char *intercepted_function_name) { 76*0fca6ea1SDimitry Andric if (InRealtimeContext() && !IsBypassed()) { 77*0fca6ea1SDimitry Andric BypassPush(); 78*0fca6ea1SDimitry Andric PrintDiagnostics(intercepted_function_name); 79*0fca6ea1SDimitry Andric InvokeViolationDetectedAction(); 80*0fca6ea1SDimitry Andric BypassPop(); 81*0fca6ea1SDimitry Andric } 82*0fca6ea1SDimitry Andric } 83*0fca6ea1SDimitry Andric 84*0fca6ea1SDimitry Andric bool __rtsan::Context::InRealtimeContext() const { return realtime_depth > 0; } 85*0fca6ea1SDimitry Andric 86*0fca6ea1SDimitry Andric bool __rtsan::Context::IsBypassed() const { return bypass_depth > 0; } 87*0fca6ea1SDimitry Andric 88*0fca6ea1SDimitry Andric void __rtsan::Context::PrintDiagnostics(const char *intercepted_function_name) { 89*0fca6ea1SDimitry Andric fprintf(stderr, 90*0fca6ea1SDimitry Andric "Real-time violation: intercepted call to real-time unsafe function " 91*0fca6ea1SDimitry Andric "`%s` in real-time context! Stack trace:\n", 92*0fca6ea1SDimitry Andric intercepted_function_name); 93*0fca6ea1SDimitry Andric __rtsan::PrintStackTrace(); 94*0fca6ea1SDimitry Andric } 95*0fca6ea1SDimitry Andric 96*0fca6ea1SDimitry Andric __rtsan::Context &__rtsan::GetContextForThisThread() { 97*0fca6ea1SDimitry Andric return GetContextForThisThreadImpl(); 98*0fca6ea1SDimitry Andric } 99