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