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
InternalFreeWrapper(void * ptr)28*0fca6ea1SDimitry Andric static void InternalFreeWrapper(void *ptr) { __sanitizer::InternalFree(ptr); }
29*0fca6ea1SDimitry Andric
GetContextForThisThreadImpl()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 */
InvokeViolationDetectedAction()62*0fca6ea1SDimitry Andric static void InvokeViolationDetectedAction() { exit(EXIT_FAILURE); }
63*0fca6ea1SDimitry Andric
64*0fca6ea1SDimitry Andric __rtsan::Context::Context() = default;
65*0fca6ea1SDimitry Andric
RealtimePush()66*0fca6ea1SDimitry Andric void __rtsan::Context::RealtimePush() { realtime_depth++; }
67*0fca6ea1SDimitry Andric
RealtimePop()68*0fca6ea1SDimitry Andric void __rtsan::Context::RealtimePop() { realtime_depth--; }
69*0fca6ea1SDimitry Andric
BypassPush()70*0fca6ea1SDimitry Andric void __rtsan::Context::BypassPush() { bypass_depth++; }
71*0fca6ea1SDimitry Andric
BypassPop()72*0fca6ea1SDimitry Andric void __rtsan::Context::BypassPop() { bypass_depth--; }
73*0fca6ea1SDimitry Andric
ExpectNotRealtime(const char * intercepted_function_name)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
InRealtimeContext() const84*0fca6ea1SDimitry Andric bool __rtsan::Context::InRealtimeContext() const { return realtime_depth > 0; }
85*0fca6ea1SDimitry Andric
IsBypassed() const86*0fca6ea1SDimitry Andric bool __rtsan::Context::IsBypassed() const { return bypass_depth > 0; }
87*0fca6ea1SDimitry Andric
PrintDiagnostics(const char * intercepted_function_name)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
GetContextForThisThread()96*0fca6ea1SDimitry Andric __rtsan::Context &__rtsan::GetContextForThisThread() {
97*0fca6ea1SDimitry Andric return GetContextForThisThreadImpl();
98*0fca6ea1SDimitry Andric }
99