Skip to content

Commit

Permalink
[compiler-rt] Realtime Sanitizer: Introduce Realtime Sanitizer (RTSan…
Browse files Browse the repository at this point in the history
…) 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]>
  • Loading branch information
cjappl and davidtrevelyan authored Jul 9, 2024
1 parent 8492ad5 commit 1adb55b
Show file tree
Hide file tree
Showing 23 changed files with 1,939 additions and 1 deletion.
4 changes: 4 additions & 0 deletions compiler-rt/CODE_OWNERS.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,7 @@ D: ThreadSanitizer
N: Bill Wendling
E: [email protected]
D: Profile runtime library

N: Christopher Apple, David Trevelyan
E: [email protected], [email protected]
D: Realtime Sanitizer (RTSan)
3 changes: 3 additions & 0 deletions compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
${LOONGARCH64})
set(ALL_ASAN_ABI_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM64_32})
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64})
set(ALL_RTSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
${LOONGARCH64})

if(ANDROID)
set(OS_NAME "Android")
Expand Down
12 changes: 11 additions & 1 deletion compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,9 @@ if(APPLE)
list_intersect(ASAN_SUPPORTED_ARCH
ALL_ASAN_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
list_intersect(RTSAN_SUPPORTED_ARCH
ALL_RTSAN_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
list_intersect(DFSAN_SUPPORTED_ARCH
ALL_DFSAN_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
Expand Down Expand Up @@ -663,6 +666,7 @@ else()
filter_available_targets(UBSAN_COMMON_SUPPORTED_ARCH
${SANITIZER_COMMON_SUPPORTED_ARCH})
filter_available_targets(ASAN_SUPPORTED_ARCH ${ALL_ASAN_SUPPORTED_ARCH})
filter_available_targets(RTSAN_SUPPORTED_ARCH ${ALL_RTSAN_SUPPORTED_ARCH})
filter_available_targets(FUZZER_SUPPORTED_ARCH ${ALL_FUZZER_SUPPORTED_ARCH})
filter_available_targets(DFSAN_SUPPORTED_ARCH ${ALL_DFSAN_SUPPORTED_ARCH})
filter_available_targets(LSAN_SUPPORTED_ARCH ${ALL_LSAN_SUPPORTED_ARCH})
Expand Down Expand Up @@ -716,7 +720,7 @@ if(COMPILER_RT_SUPPORTED_ARCH)
endif()
message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")

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

if (COMPILER_RT_HAS_SANITIZER_COMMON AND RTSAN_SUPPORTED_ARCH)
set(COMPILER_RT_HAS_RTSAN TRUE)
else()
set(COMPILER_RT_HAS_RTSAN FALSE)
endif()

if (OS_NAME MATCHES "Linux|FreeBSD|Windows|NetBSD|SunOS")
set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME TRUE)
else()
Expand Down
92 changes: 92 additions & 0 deletions compiler-rt/lib/rtsan/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
include_directories(..)

set(RTSAN_CXX_SOURCES
rtsan.cpp
rtsan_context.cpp
rtsan_stack.cpp
rtsan_interceptors.cpp)

set(RTSAN_PREINIT_SOURCES
rtsan_preinit.cpp)

set(RTSAN_HEADERS
rtsan.h
rtsan_context.h
rtsan_stack.h)

set(RTSAN_DEPS)

set(RTSAN_CFLAGS
${COMPILER_RT_COMMON_CFLAGS}
${COMPILER_RT_CXX_CFLAGS}
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS)
set(RTSAN_LINK_FLAGS ${COMPILER_RT_COMMON_LINK_FLAGS})
set(RTSAN_LINK_LIBS
${COMPILER_RT_UNWINDER_LINK_LIBS}
${COMPILER_RT_CXX_LINK_LIBS})

if(APPLE)
add_compiler_rt_object_libraries(RTRtsan
OS ${SANITIZER_COMMON_SUPPORTED_OS}
ARCHS ${RTSAN_SUPPORTED_ARCH}
SOURCES ${RTSAN_CXX_SOURCES}
ADDITIONAL_HEADERS ${RTSAN_HEADERS}
CFLAGS ${RTSAN_CFLAGS}
DEPS ${RTSAN_DEPS})
else()
add_compiler_rt_object_libraries(RTRtsan
ARCHS ${RTSAN_SUPPORTED_ARCH}
SOURCES ${RTSAN_CXX_SOURCES}
ADDITIONAL_HEADERS ${RTSAN_HEADERS}
CFLAGS ${RTSAN_CFLAGS}
DEPS ${RTSAN_DEPS})
add_compiler_rt_object_libraries(RTRtsan_preinit
ARCHS ${RTSAN_SUPPORTED_ARCH}
SOURCES ${RTSAN_PREINIT_SOURCES}
ADDITIONAL_HEADERS ${RTSAN_HEADERS}
CFLAGS ${RTSAN_CFLAGS})
endif()

set(RTSAN_COMMON_RUNTIME_OBJECT_LIBS
RTInterception
RTSanitizerCommon
RTSanitizerCommonLibc
RTSanitizerCommonCoverage
RTSanitizerCommonSymbolizer)

append_list_if(COMPILER_RT_HAS_LIBDL dl RTSAN_LINK_LIBS)
append_list_if(COMPILER_RT_HAS_LIBRT rt RTSAN_LINK_LIBS)
append_list_if(COMPILER_RT_HAS_LIBM m RTSAN_LINK_LIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread RTSAN_LINK_LIBS)
append_list_if(COMPILER_RT_HAS_LIBLOG log RTSAN_LINK_LIBS)

add_compiler_rt_component(rtsan)

if (APPLE)
add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
set(RTSAN_LINK_FLAGS ${RTSAN_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS})

add_compiler_rt_runtime(clang_rt.rtsan
SHARED
OS ${SANITIZER_COMMON_SUPPORTED_OS}
ARCHS ${RTSAN_SUPPORTED_ARCH}
OBJECT_LIBS RTRtsan
${RTSAN_COMMON_RUNTIME_OBJECT_LIBS}
LINK_FLAGS ${RTSAN_LINK_FLAGS}
LINK_LIBS ${RTSAN_LINK_LIBS}
PARENT_TARGET rtsan)
else()
add_compiler_rt_runtime(clang_rt.rtsan
STATIC
ARCHS ${RTSAN_SUPPORTED_ARCH}
OBJECT_LIBS RTRtsan_preinit
RTRtsan
${RTSAN_COMMON_RUNTIME_OBJECT_LIBS}
LINK_FLAGS ${RTSAN_LINK_FLAGS}
CFLAGS ${RTSAN_CFLAGS}
PARENT_TARGET rtsan)
endif()

if(COMPILER_RT_INCLUDE_TESTS)
add_subdirectory(tests)
endif()
37 changes: 37 additions & 0 deletions compiler-rt/lib/rtsan/rtsan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===--- rtsan.cpp - Realtime Sanitizer -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include <rtsan/rtsan.h>
#include <rtsan/rtsan_context.h>
#include <rtsan/rtsan_interceptors.h>

extern "C" {

SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() {
__rtsan::InitializeInterceptors();
}

SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() {
__rtsan::GetContextForThisThread().RealtimePush();
}

SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() {
__rtsan::GetContextForThisThread().RealtimePop();
}

SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_off() {
__rtsan::GetContextForThisThread().BypassPush();
}

SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_on() {
__rtsan::GetContextForThisThread().BypassPop();
}

} // extern "C"
40 changes: 40 additions & 0 deletions compiler-rt/lib/rtsan/rtsan.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===--- rtsan.h - Realtime Sanitizer ---------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#pragma once

#include "sanitizer_common/sanitizer_internal_defs.h"

extern "C" {

// Initialise rtsan interceptors.
// A call to this method is added to the preinit array on Linux systems.
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init();

// Enter real-time context.
// When in a real-time context, RTSan interceptors will error if realtime
// violations are detected. Calls to this method are injected at the code
// generation stage when RTSan is enabled.
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter();

// Exit the real-time context.
// When not in a real-time context, RTSan interceptors will simply forward
// intercepted method calls to the real methods.
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit();

// Disable all RTSan error reporting.
// Injected into the code if "nosanitize(realtime)" is on a function.
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_off();

// Re-enable all RTSan error reporting.
// The counterpart to `__rtsan_off`.
SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_on();

} // extern "C"
98 changes: 98 additions & 0 deletions compiler-rt/lib/rtsan/rtsan_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//===--- rtsan_context.cpp - Realtime Sanitizer -----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include <rtsan/rtsan_context.h>

#include <rtsan/rtsan_stack.h>

#include <sanitizer_common/sanitizer_allocator_internal.h>
#include <sanitizer_common/sanitizer_stacktrace.h>

#include <new>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

static pthread_key_t context_key;
static pthread_once_t key_once = PTHREAD_ONCE_INIT;

// InternalFree cannot be passed directly to pthread_key_create
// because it expects a signature with only one arg
static void InternalFreeWrapper(void *ptr) { __sanitizer::InternalFree(ptr); }

static __rtsan::Context &GetContextForThisThreadImpl() {
auto make_thread_local_context_key = []() {
CHECK_EQ(pthread_key_create(&context_key, InternalFreeWrapper), 0);
};

pthread_once(&key_once, make_thread_local_context_key);
__rtsan::Context *current_thread_context =
static_cast<__rtsan::Context *>(pthread_getspecific(context_key));
if (current_thread_context == nullptr) {
current_thread_context = static_cast<__rtsan::Context *>(
__sanitizer::InternalAlloc(sizeof(__rtsan::Context)));
new (current_thread_context) __rtsan::Context();
pthread_setspecific(context_key, current_thread_context);
}

return *current_thread_context;
}

/*
This is a placeholder stub for a future feature that will allow
a user to configure RTSan's behaviour when a real-time safety
violation is detected. The RTSan developers intend for the
following choices to be made available, via a RTSAN_OPTIONS
environment variable, in a future PR:
i) exit,
ii) continue, or
iii) wait for user input from stdin.
Until then, and to keep the first PRs small, only the exit mode
is available.
*/
static void InvokeViolationDetectedAction() { exit(EXIT_FAILURE); }

__rtsan::Context::Context() = default;

void __rtsan::Context::RealtimePush() { realtime_depth++; }

void __rtsan::Context::RealtimePop() { realtime_depth--; }

void __rtsan::Context::BypassPush() { bypass_depth++; }

void __rtsan::Context::BypassPop() { bypass_depth--; }

void __rtsan::Context::ExpectNotRealtime(
const char *intercepted_function_name) {
if (InRealtimeContext() && !IsBypassed()) {
BypassPush();
PrintDiagnostics(intercepted_function_name);
InvokeViolationDetectedAction();
BypassPop();
}
}

bool __rtsan::Context::InRealtimeContext() const { return realtime_depth > 0; }

bool __rtsan::Context::IsBypassed() const { return bypass_depth > 0; }

void __rtsan::Context::PrintDiagnostics(const char *intercepted_function_name) {
fprintf(stderr,
"Real-time violation: intercepted call to real-time unsafe function "
"`%s` in real-time context! Stack trace:\n",
intercepted_function_name);
__rtsan::PrintStackTrace();
}

__rtsan::Context &__rtsan::GetContextForThisThread() {
return GetContextForThisThreadImpl();
}
38 changes: 38 additions & 0 deletions compiler-rt/lib/rtsan/rtsan_context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- rtsan_context.h - Realtime Sanitizer -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#pragma once

namespace __rtsan {

class Context {
public:
Context();

void RealtimePush();
void RealtimePop();

void BypassPush();
void BypassPop();

void ExpectNotRealtime(const char *intercepted_function_name);

private:
bool InRealtimeContext() const;
bool IsBypassed() const;
void PrintDiagnostics(const char *intercepted_function_name);

int realtime_depth{0};
int bypass_depth{0};
};

Context &GetContextForThisThread();

} // namespace __rtsan
Loading

0 comments on commit 1adb55b

Please sign in to comment.