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(p: 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(once_control: &key_once, init_routine: make_thread_local_context_key); |
36 | __rtsan::Context *current_thread_context = |
37 | static_cast<__rtsan::Context *>(pthread_getspecific(key: context_key)); |
38 | if (current_thread_context == nullptr) { |
39 | current_thread_context = static_cast<__rtsan::Context *>( |
40 | __sanitizer::InternalAlloc(size: sizeof(__rtsan::Context))); |
41 | new (current_thread_context) __rtsan::Context(); |
42 | pthread_setspecific(key: context_key, pointer: 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 | format: "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 | } |
99 | |