Skip to content

Commit 1adb55b

Browse files
[compiler-rt] Realtime Sanitizer: Introduce Realtime Sanitizer (RTSan) backend (#92460)
Introducing the main runtime of realtime sanitizer. For more information, please see the [discourse thread](https://discourse.llvm.org/t/rfc-nolock-and-noalloc-attributes/76837). We have also put together a [reviewer support document](https://github.com/realtime-sanitizer/radsan/blob/doc/review-support/doc/review.md) to show what our intention is. This review introduces the sanitizer backend. This includes: * CMake build files (largely adapted from asan). * Main RTSan architecture (the external API, thread local context, stack). * Interceptors. * Many unit tests. Please see the [reviewer support document](https://github.com/realtime-sanitizer/radsan/blob/doc/review-support/doc/review.md) for what our next steps are. We are moving in lockstep with this PR #84983 for the codegen coming up next. Note to reviewers: If you see support documentation mention "RADSan", this was the "old acronym" for the realtime sanitizer, they refer to the same thing. If you see it let us know and we can correct it (especially in the llvm codebase) --------- Co-authored-by: David Trevelyan <[email protected]>
1 parent 8492ad5 commit 1adb55b

23 files changed

+1939
-1
lines changed

compiler-rt/CODE_OWNERS.TXT

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@ D: ThreadSanitizer
6767
N: Bill Wendling
6868
6969
D: Profile runtime library
70+
71+
N: Christopher Apple, David Trevelyan
72+
73+
D: Realtime Sanitizer (RTSan)

compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
3232
${LOONGARCH64})
3333
set(ALL_ASAN_ABI_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM64_32})
3434
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64})
35+
set(ALL_RTSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
36+
${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
37+
${LOONGARCH64})
3538

3639
if(ANDROID)
3740
set(OS_NAME "Android")

compiler-rt/cmake/config-ix.cmake

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,9 @@ if(APPLE)
597597
list_intersect(ASAN_SUPPORTED_ARCH
598598
ALL_ASAN_SUPPORTED_ARCH
599599
SANITIZER_COMMON_SUPPORTED_ARCH)
600+
list_intersect(RTSAN_SUPPORTED_ARCH
601+
ALL_RTSAN_SUPPORTED_ARCH
602+
SANITIZER_COMMON_SUPPORTED_ARCH)
600603
list_intersect(DFSAN_SUPPORTED_ARCH
601604
ALL_DFSAN_SUPPORTED_ARCH
602605
SANITIZER_COMMON_SUPPORTED_ARCH)
@@ -663,6 +666,7 @@ else()
663666
filter_available_targets(UBSAN_COMMON_SUPPORTED_ARCH
664667
${SANITIZER_COMMON_SUPPORTED_ARCH})
665668
filter_available_targets(ASAN_SUPPORTED_ARCH ${ALL_ASAN_SUPPORTED_ARCH})
669+
filter_available_targets(RTSAN_SUPPORTED_ARCH ${ALL_RTSAN_SUPPORTED_ARCH})
666670
filter_available_targets(FUZZER_SUPPORTED_ARCH ${ALL_FUZZER_SUPPORTED_ARCH})
667671
filter_available_targets(DFSAN_SUPPORTED_ARCH ${ALL_DFSAN_SUPPORTED_ARCH})
668672
filter_available_targets(LSAN_SUPPORTED_ARCH ${ALL_LSAN_SUPPORTED_ARCH})
@@ -716,7 +720,7 @@ if(COMPILER_RT_SUPPORTED_ARCH)
716720
endif()
717721
message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")
718722

719-
set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo_standalone;ubsan_minimal;gwp_asan;nsan;asan_abi)
723+
set(ALL_SANITIZERS asan;rtsan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo_standalone;ubsan_minimal;gwp_asan;nsan;asan_abi)
720724
set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING
721725
"sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
722726
list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
@@ -747,6 +751,12 @@ else()
747751
set(COMPILER_RT_HAS_ASAN FALSE)
748752
endif()
749753

754+
if (COMPILER_RT_HAS_SANITIZER_COMMON AND RTSAN_SUPPORTED_ARCH)
755+
set(COMPILER_RT_HAS_RTSAN TRUE)
756+
else()
757+
set(COMPILER_RT_HAS_RTSAN FALSE)
758+
endif()
759+
750760
if (OS_NAME MATCHES "Linux|FreeBSD|Windows|NetBSD|SunOS")
751761
set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME TRUE)
752762
else()

compiler-rt/lib/rtsan/CMakeLists.txt

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
include_directories(..)
2+
3+
set(RTSAN_CXX_SOURCES
4+
rtsan.cpp
5+
rtsan_context.cpp
6+
rtsan_stack.cpp
7+
rtsan_interceptors.cpp)
8+
9+
set(RTSAN_PREINIT_SOURCES
10+
rtsan_preinit.cpp)
11+
12+
set(RTSAN_HEADERS
13+
rtsan.h
14+
rtsan_context.h
15+
rtsan_stack.h)
16+
17+
set(RTSAN_DEPS)
18+
19+
set(RTSAN_CFLAGS
20+
${COMPILER_RT_COMMON_CFLAGS}
21+
${COMPILER_RT_CXX_CFLAGS}
22+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS)
23+
set(RTSAN_LINK_FLAGS ${COMPILER_RT_COMMON_LINK_FLAGS})
24+
set(RTSAN_LINK_LIBS
25+
${COMPILER_RT_UNWINDER_LINK_LIBS}
26+
${COMPILER_RT_CXX_LINK_LIBS})
27+
28+
if(APPLE)
29+
add_compiler_rt_object_libraries(RTRtsan
30+
OS ${SANITIZER_COMMON_SUPPORTED_OS}
31+
ARCHS ${RTSAN_SUPPORTED_ARCH}
32+
SOURCES ${RTSAN_CXX_SOURCES}
33+
ADDITIONAL_HEADERS ${RTSAN_HEADERS}
34+
CFLAGS ${RTSAN_CFLAGS}
35+
DEPS ${RTSAN_DEPS})
36+
else()
37+
add_compiler_rt_object_libraries(RTRtsan
38+
ARCHS ${RTSAN_SUPPORTED_ARCH}
39+
SOURCES ${RTSAN_CXX_SOURCES}
40+
ADDITIONAL_HEADERS ${RTSAN_HEADERS}
41+
CFLAGS ${RTSAN_CFLAGS}
42+
DEPS ${RTSAN_DEPS})
43+
add_compiler_rt_object_libraries(RTRtsan_preinit
44+
ARCHS ${RTSAN_SUPPORTED_ARCH}
45+
SOURCES ${RTSAN_PREINIT_SOURCES}
46+
ADDITIONAL_HEADERS ${RTSAN_HEADERS}
47+
CFLAGS ${RTSAN_CFLAGS})
48+
endif()
49+
50+
set(RTSAN_COMMON_RUNTIME_OBJECT_LIBS
51+
RTInterception
52+
RTSanitizerCommon
53+
RTSanitizerCommonLibc
54+
RTSanitizerCommonCoverage
55+
RTSanitizerCommonSymbolizer)
56+
57+
append_list_if(COMPILER_RT_HAS_LIBDL dl RTSAN_LINK_LIBS)
58+
append_list_if(COMPILER_RT_HAS_LIBRT rt RTSAN_LINK_LIBS)
59+
append_list_if(COMPILER_RT_HAS_LIBM m RTSAN_LINK_LIBS)
60+
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread RTSAN_LINK_LIBS)
61+
append_list_if(COMPILER_RT_HAS_LIBLOG log RTSAN_LINK_LIBS)
62+
63+
add_compiler_rt_component(rtsan)
64+
65+
if (APPLE)
66+
add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
67+
set(RTSAN_LINK_FLAGS ${RTSAN_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS})
68+
69+
add_compiler_rt_runtime(clang_rt.rtsan
70+
SHARED
71+
OS ${SANITIZER_COMMON_SUPPORTED_OS}
72+
ARCHS ${RTSAN_SUPPORTED_ARCH}
73+
OBJECT_LIBS RTRtsan
74+
${RTSAN_COMMON_RUNTIME_OBJECT_LIBS}
75+
LINK_FLAGS ${RTSAN_LINK_FLAGS}
76+
LINK_LIBS ${RTSAN_LINK_LIBS}
77+
PARENT_TARGET rtsan)
78+
else()
79+
add_compiler_rt_runtime(clang_rt.rtsan
80+
STATIC
81+
ARCHS ${RTSAN_SUPPORTED_ARCH}
82+
OBJECT_LIBS RTRtsan_preinit
83+
RTRtsan
84+
${RTSAN_COMMON_RUNTIME_OBJECT_LIBS}
85+
LINK_FLAGS ${RTSAN_LINK_FLAGS}
86+
CFLAGS ${RTSAN_CFLAGS}
87+
PARENT_TARGET rtsan)
88+
endif()
89+
90+
if(COMPILER_RT_INCLUDE_TESTS)
91+
add_subdirectory(tests)
92+
endif()

compiler-rt/lib/rtsan/rtsan.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===--- rtsan.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.h>
12+
#include <rtsan/rtsan_context.h>
13+
#include <rtsan/rtsan_interceptors.h>
14+
15+
extern "C" {
16+
17+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() {
18+
__rtsan::InitializeInterceptors();
19+
}
20+
21+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() {
22+
__rtsan::GetContextForThisThread().RealtimePush();
23+
}
24+
25+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() {
26+
__rtsan::GetContextForThisThread().RealtimePop();
27+
}
28+
29+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_off() {
30+
__rtsan::GetContextForThisThread().BypassPush();
31+
}
32+
33+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_on() {
34+
__rtsan::GetContextForThisThread().BypassPop();
35+
}
36+
37+
} // extern "C"

compiler-rt/lib/rtsan/rtsan.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- rtsan.h - 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+
#pragma once
12+
13+
#include "sanitizer_common/sanitizer_internal_defs.h"
14+
15+
extern "C" {
16+
17+
// Initialise rtsan interceptors.
18+
// A call to this method is added to the preinit array on Linux systems.
19+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init();
20+
21+
// Enter real-time context.
22+
// When in a real-time context, RTSan interceptors will error if realtime
23+
// violations are detected. Calls to this method are injected at the code
24+
// generation stage when RTSan is enabled.
25+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter();
26+
27+
// Exit the real-time context.
28+
// When not in a real-time context, RTSan interceptors will simply forward
29+
// intercepted method calls to the real methods.
30+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit();
31+
32+
// Disable all RTSan error reporting.
33+
// Injected into the code if "nosanitize(realtime)" is on a function.
34+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_off();
35+
36+
// Re-enable all RTSan error reporting.
37+
// The counterpart to `__rtsan_off`.
38+
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_on();
39+
40+
} // extern "C"
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
}

compiler-rt/lib/rtsan/rtsan_context.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===--- rtsan_context.h - 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+
#pragma once
12+
13+
namespace __rtsan {
14+
15+
class Context {
16+
public:
17+
Context();
18+
19+
void RealtimePush();
20+
void RealtimePop();
21+
22+
void BypassPush();
23+
void BypassPop();
24+
25+
void ExpectNotRealtime(const char *intercepted_function_name);
26+
27+
private:
28+
bool InRealtimeContext() const;
29+
bool IsBypassed() const;
30+
void PrintDiagnostics(const char *intercepted_function_name);
31+
32+
int realtime_depth{0};
33+
int bypass_depth{0};
34+
};
35+
36+
Context &GetContextForThisThread();
37+
38+
} // namespace __rtsan

0 commit comments

Comments
 (0)