aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Apple <cja-private@pm.me>2024-07-09 11:00:38 -0700
committerGitHub <noreply@github.com>2024-07-09 11:00:38 -0700
commit1adb55b1c4bfd21752e6b2718c3c4b8abb203d09 (patch)
treea395d7d8931339db84065f86fdb51a01bba84ba9
parent8492ad5964606c0b96e32c697def7e9701bb5d5a (diff)
downloadllvm-1adb55b1c4bfd21752e6b2718c3c4b8abb203d09.zip
llvm-1adb55b1c4bfd21752e6b2718c3c4b8abb203d09.tar.gz
llvm-1adb55b1c4bfd21752e6b2718c3c4b8abb203d09.tar.bz2
[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 <david.trevelyan@gmail.com>
-rw-r--r--compiler-rt/CODE_OWNERS.TXT4
-rw-r--r--compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake3
-rw-r--r--compiler-rt/cmake/config-ix.cmake12
-rw-r--r--compiler-rt/lib/rtsan/CMakeLists.txt92
-rw-r--r--compiler-rt/lib/rtsan/rtsan.cpp37
-rw-r--r--compiler-rt/lib/rtsan/rtsan.h40
-rw-r--r--compiler-rt/lib/rtsan/rtsan_context.cpp98
-rw-r--r--compiler-rt/lib/rtsan/rtsan_context.h38
-rw-r--r--compiler-rt/lib/rtsan/rtsan_interceptors.cpp405
-rw-r--r--compiler-rt/lib/rtsan/rtsan_interceptors.h15
-rw-r--r--compiler-rt/lib/rtsan/rtsan_preinit.cpp23
-rw-r--r--compiler-rt/lib/rtsan/rtsan_stack.cpp50
-rw-r--r--compiler-rt/lib/rtsan/rtsan_stack.h15
-rw-r--r--compiler-rt/lib/rtsan/tests/CMakeLists.txt103
-rw-r--r--compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp69
-rw-r--r--compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp207
-rw-r--r--compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp516
-rw-r--r--compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp17
-rw-r--r--compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h47
-rw-r--r--compiler-rt/test/rtsan/CMakeLists.txt58
-rw-r--r--compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in25
-rw-r--r--compiler-rt/test/rtsan/lit.cfg.py49
-rw-r--r--compiler-rt/test/rtsan/lit.site.cfg.py.in17
23 files changed, 1939 insertions, 1 deletions
diff --git a/compiler-rt/CODE_OWNERS.TXT b/compiler-rt/CODE_OWNERS.TXT
index ad136ed..570ab86 100644
--- a/compiler-rt/CODE_OWNERS.TXT
+++ b/compiler-rt/CODE_OWNERS.TXT
@@ -67,3 +67,7 @@ D: ThreadSanitizer
N: Bill Wendling
E: isanbard@gmail.com
D: Profile runtime library
+
+N: Christopher Apple, David Trevelyan
+E: cja-private@pm.me, realtime.sanitizer@gmail.com
+D: Realtime Sanitizer (RTSan)
diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
index ac4a712..bc152e3 100644
--- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
+++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
@@ -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")
diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
index 7037bf8..1130f9f 100644
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -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)
@@ -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})
@@ -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}")
@@ -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()
diff --git a/compiler-rt/lib/rtsan/CMakeLists.txt b/compiler-rt/lib/rtsan/CMakeLists.txt
new file mode 100644
index 0000000..bd7358e
--- /dev/null
+++ b/compiler-rt/lib/rtsan/CMakeLists.txt
@@ -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()
diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp
new file mode 100644
index 0000000..43a63b4
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan.cpp
@@ -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"
diff --git a/compiler-rt/lib/rtsan/rtsan.h b/compiler-rt/lib/rtsan/rtsan.h
new file mode 100644
index 0000000..8b1219c
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan.h
@@ -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"
diff --git a/compiler-rt/lib/rtsan/rtsan_context.cpp b/compiler-rt/lib/rtsan/rtsan_context.cpp
new file mode 100644
index 0000000..f761dde
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_context.cpp
@@ -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();
+}
diff --git a/compiler-rt/lib/rtsan/rtsan_context.h b/compiler-rt/lib/rtsan/rtsan_context.h
new file mode 100644
index 0000000..515bb8a
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_context.h
@@ -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
diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors.cpp
new file mode 100644
index 0000000..3a65f9d3
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors.cpp
@@ -0,0 +1,405 @@
+//===--- rtsan_interceptors.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_interceptors.h"
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+
+#include "interception/interception.h"
+#include "rtsan/rtsan_context.h"
+
+#if SANITIZER_APPLE
+#include <libkern/OSAtomic.h>
+#include <os/lock.h>
+#endif
+
+#if SANITIZER_INTERCEPT_MEMALIGN || SANITIZER_INTERCEPT_PVALLOC
+#include <malloc.h>
+#endif
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+
+using namespace __sanitizer;
+
+void ExpectNotRealtime(const char *intercepted_function_name) {
+ __rtsan::GetContextForThisThread().ExpectNotRealtime(
+ intercepted_function_name);
+}
+
+// Filesystem
+
+INTERCEPTOR(int, open, const char *path, int oflag, ...) {
+ // TODO Establish whether we should intercept here if the flag contains
+ // O_NONBLOCK
+ ExpectNotRealtime("open");
+
+ va_list args;
+ va_start(args, oflag);
+ const mode_t mode = va_arg(args, int);
+ va_end(args);
+
+ const int result = REAL(open)(path, oflag, mode);
+ return result;
+}
+
+INTERCEPTOR(int, openat, int fd, const char *path, int oflag, ...) {
+ // TODO Establish whether we should intercept here if the flag contains
+ // O_NONBLOCK
+ ExpectNotRealtime("openat");
+
+ va_list args;
+ va_start(args, oflag);
+ mode_t mode = va_arg(args, int);
+ va_end(args);
+
+ const int result = REAL(openat)(fd, path, oflag, mode);
+ return result;
+}
+
+INTERCEPTOR(int, creat, const char *path, mode_t mode) {
+ // TODO Establish whether we should intercept here if the flag contains
+ // O_NONBLOCK
+ ExpectNotRealtime("creat");
+ const int result = REAL(creat)(path, mode);
+ return result;
+}
+
+INTERCEPTOR(int, fcntl, int filedes, int cmd, ...) {
+ ExpectNotRealtime("fcntl");
+
+ va_list args;
+ va_start(args, cmd);
+
+ // Following precedent here. The linux source (fcntl.c, do_fcntl) accepts the
+ // final argument in a variable that will hold the largest of the possible
+ // argument types (pointers and ints are typical in fcntl) It is then assumed
+ // that the implementation of fcntl will cast it properly depending on cmd.
+ //
+ // This is also similar to what is done in
+ // sanitizer_common/sanitizer_common_syscalls.inc
+ const unsigned long arg = va_arg(args, unsigned long);
+ int result = REAL(fcntl)(filedes, cmd, arg);
+
+ va_end(args);
+
+ return result;
+}
+
+INTERCEPTOR(int, close, int filedes) {
+ ExpectNotRealtime("close");
+ return REAL(close)(filedes);
+}
+
+INTERCEPTOR(FILE *, fopen, const char *path, const char *mode) {
+ ExpectNotRealtime("fopen");
+ return REAL(fopen)(path, mode);
+}
+
+INTERCEPTOR(size_t, fread, void *ptr, size_t size, size_t nitems,
+ FILE *stream) {
+ ExpectNotRealtime("fread");
+ return REAL(fread)(ptr, size, nitems, stream);
+}
+
+INTERCEPTOR(size_t, fwrite, const void *ptr, size_t size, size_t nitems,
+ FILE *stream) {
+ ExpectNotRealtime("fwrite");
+ return REAL(fwrite)(ptr, size, nitems, stream);
+}
+
+INTERCEPTOR(int, fclose, FILE *stream) {
+ ExpectNotRealtime("fclose");
+ return REAL(fclose)(stream);
+}
+
+INTERCEPTOR(int, fputs, const char *s, FILE *stream) {
+ ExpectNotRealtime("fputs");
+ return REAL(fputs)(s, stream);
+}
+
+// Streams
+INTERCEPTOR(int, puts, const char *s) {
+ ExpectNotRealtime("puts");
+ return REAL(puts)(s);
+}
+
+// Concurrency
+#if SANITIZER_APPLE
+#pragma clang diagnostic push
+// OSSpinLockLock is deprecated, but still in use in libc++
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) {
+ ExpectNotRealtime("OSSpinLockLock");
+ return REAL(OSSpinLockLock)(lock);
+}
+#pragma clang diagnostic pop
+
+INTERCEPTOR(void, os_unfair_lock_lock, os_unfair_lock_t lock) {
+ ExpectNotRealtime("os_unfair_lock_lock");
+ return REAL(os_unfair_lock_lock)(lock);
+}
+#elif SANITIZER_LINUX
+INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *spinlock) {
+ ExpectNotRealtime("pthread_spin_lock");
+ return REAL(pthread_spin_lock)(spinlock);
+}
+#endif
+
+INTERCEPTOR(int, pthread_create, pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg) {
+ ExpectNotRealtime("pthread_create");
+ return REAL(pthread_create)(thread, attr, start_routine, arg);
+}
+
+INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *mutex) {
+ ExpectNotRealtime("pthread_mutex_lock");
+ return REAL(pthread_mutex_lock)(mutex);
+}
+
+INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *mutex) {
+ ExpectNotRealtime("pthread_mutex_unlock");
+ return REAL(pthread_mutex_unlock)(mutex);
+}
+
+INTERCEPTOR(int, pthread_join, pthread_t thread, void **value_ptr) {
+ ExpectNotRealtime("pthread_join");
+ return REAL(pthread_join)(thread, value_ptr);
+}
+
+INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *cond) {
+ ExpectNotRealtime("pthread_cond_signal");
+ return REAL(pthread_cond_signal)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *cond) {
+ ExpectNotRealtime("pthread_cond_broadcast");
+ return REAL(pthread_cond_broadcast)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *cond,
+ pthread_mutex_t *mutex) {
+ ExpectNotRealtime("pthread_cond_wait");
+ return REAL(pthread_cond_wait)(cond, mutex);
+}
+
+INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *cond,
+ pthread_mutex_t *mutex, const timespec *ts) {
+ ExpectNotRealtime("pthread_cond_timedwait");
+ return REAL(pthread_cond_timedwait)(cond, mutex, ts);
+}
+
+INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *lock) {
+ ExpectNotRealtime("pthread_rwlock_rdlock");
+ return REAL(pthread_rwlock_rdlock)(lock);
+}
+
+INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *lock) {
+ ExpectNotRealtime("pthread_rwlock_unlock");
+ return REAL(pthread_rwlock_unlock)(lock);
+}
+
+INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *lock) {
+ ExpectNotRealtime("pthread_rwlock_wrlock");
+ return REAL(pthread_rwlock_wrlock)(lock);
+}
+
+// Sleeping
+
+INTERCEPTOR(unsigned int, sleep, unsigned int s) {
+ ExpectNotRealtime("sleep");
+ return REAL(sleep)(s);
+}
+
+INTERCEPTOR(int, usleep, useconds_t u) {
+ ExpectNotRealtime("usleep");
+ return REAL(usleep)(u);
+}
+
+INTERCEPTOR(int, nanosleep, const struct timespec *rqtp,
+ struct timespec *rmtp) {
+ ExpectNotRealtime("nanosleep");
+ return REAL(nanosleep)(rqtp, rmtp);
+}
+
+// Memory
+
+INTERCEPTOR(void *, calloc, SIZE_T num, SIZE_T size) {
+ ExpectNotRealtime("calloc");
+ return REAL(calloc)(num, size);
+}
+
+INTERCEPTOR(void, free, void *ptr) {
+ if (ptr != NULL) {
+ ExpectNotRealtime("free");
+ }
+ return REAL(free)(ptr);
+}
+
+INTERCEPTOR(void *, malloc, SIZE_T size) {
+ ExpectNotRealtime("malloc");
+ return REAL(malloc)(size);
+}
+
+INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
+ ExpectNotRealtime("realloc");
+ return REAL(realloc)(ptr, size);
+}
+
+INTERCEPTOR(void *, reallocf, void *ptr, SIZE_T size) {
+ ExpectNotRealtime("reallocf");
+ return REAL(reallocf)(ptr, size);
+}
+
+INTERCEPTOR(void *, valloc, SIZE_T size) {
+ ExpectNotRealtime("valloc");
+ return REAL(valloc)(size);
+}
+
+#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
+INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
+ ExpectNotRealtime("aligned_alloc");
+ return REAL(aligned_alloc)(alignment, size);
+}
+#define RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc)
+#else
+#define RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC
+#endif
+
+INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
+ ExpectNotRealtime("posix_memalign");
+ return REAL(posix_memalign)(memptr, alignment, size);
+}
+
+#if SANITIZER_INTERCEPT_MEMALIGN
+INTERCEPTOR(void *, memalign, size_t alignment, size_t size) {
+ ExpectNotRealtime("memalign");
+ return REAL(memalign)(alignment, size);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_PVALLOC
+INTERCEPTOR(void *, pvalloc, size_t size) {
+ ExpectNotRealtime("pvalloc");
+ return REAL(pvalloc)(size);
+}
+#endif
+
+// Sockets
+INTERCEPTOR(int, socket, int domain, int type, int protocol) {
+ ExpectNotRealtime("socket");
+ return REAL(socket)(domain, type, protocol);
+}
+
+INTERCEPTOR(ssize_t, send, int sockfd, const void *buf, size_t len, int flags) {
+ ExpectNotRealtime("send");
+ return REAL(send)(sockfd, buf, len, flags);
+}
+
+INTERCEPTOR(ssize_t, sendmsg, int socket, const struct msghdr *message,
+ int flags) {
+ ExpectNotRealtime("sendmsg");
+ return REAL(sendmsg)(socket, message, flags);
+}
+
+INTERCEPTOR(ssize_t, sendto, int socket, const void *buffer, size_t length,
+ int flags, const struct sockaddr *dest_addr, socklen_t dest_len) {
+ ExpectNotRealtime("sendto");
+ return REAL(sendto)(socket, buffer, length, flags, dest_addr, dest_len);
+}
+
+INTERCEPTOR(ssize_t, recv, int socket, void *buffer, size_t length, int flags) {
+ ExpectNotRealtime("recv");
+ return REAL(recv)(socket, buffer, length, flags);
+}
+
+INTERCEPTOR(ssize_t, recvfrom, int socket, void *buffer, size_t length,
+ int flags, struct sockaddr *address, socklen_t *address_len) {
+ ExpectNotRealtime("recvfrom");
+ return REAL(recvfrom)(socket, buffer, length, flags, address, address_len);
+}
+
+INTERCEPTOR(ssize_t, recvmsg, int socket, struct msghdr *message, int flags) {
+ ExpectNotRealtime("recvmsg");
+ return REAL(recvmsg)(socket, message, flags);
+}
+
+INTERCEPTOR(int, shutdown, int socket, int how) {
+ ExpectNotRealtime("shutdown");
+ return REAL(shutdown)(socket, how);
+}
+
+// Preinit
+void __rtsan::InitializeInterceptors() {
+ INTERCEPT_FUNCTION(calloc);
+ INTERCEPT_FUNCTION(free);
+ INTERCEPT_FUNCTION(malloc);
+ INTERCEPT_FUNCTION(realloc);
+ INTERCEPT_FUNCTION(reallocf);
+ INTERCEPT_FUNCTION(valloc);
+ RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC;
+ INTERCEPT_FUNCTION(posix_memalign);
+#if SANITIZER_INTERCEPT_MEMALIGN
+ INTERCEPT_FUNCTION(memalign);
+#endif
+#if SANITIZER_INTERCEPT_PVALLOC
+ INTERCEPT_FUNCTION(pvalloc);
+#endif
+
+ INTERCEPT_FUNCTION(open);
+ INTERCEPT_FUNCTION(openat);
+ INTERCEPT_FUNCTION(close);
+ INTERCEPT_FUNCTION(fopen);
+ INTERCEPT_FUNCTION(fread);
+ INTERCEPT_FUNCTION(fwrite);
+ INTERCEPT_FUNCTION(fclose);
+ INTERCEPT_FUNCTION(fcntl);
+ INTERCEPT_FUNCTION(creat);
+ INTERCEPT_FUNCTION(puts);
+ INTERCEPT_FUNCTION(fputs);
+
+#if SANITIZER_APPLE
+ INTERCEPT_FUNCTION(OSSpinLockLock);
+ INTERCEPT_FUNCTION(os_unfair_lock_lock);
+#elif SANITIZER_LINUX
+ INTERCEPT_FUNCTION(pthread_spin_lock);
+#endif
+
+ INTERCEPT_FUNCTION(pthread_create);
+ INTERCEPT_FUNCTION(pthread_mutex_lock);
+ INTERCEPT_FUNCTION(pthread_mutex_unlock);
+ INTERCEPT_FUNCTION(pthread_join);
+ INTERCEPT_FUNCTION(pthread_cond_signal);
+ INTERCEPT_FUNCTION(pthread_cond_broadcast);
+ INTERCEPT_FUNCTION(pthread_cond_wait);
+ INTERCEPT_FUNCTION(pthread_cond_timedwait);
+ INTERCEPT_FUNCTION(pthread_rwlock_rdlock);
+ INTERCEPT_FUNCTION(pthread_rwlock_unlock);
+ INTERCEPT_FUNCTION(pthread_rwlock_wrlock);
+
+ INTERCEPT_FUNCTION(sleep);
+ INTERCEPT_FUNCTION(usleep);
+ INTERCEPT_FUNCTION(nanosleep);
+
+ INTERCEPT_FUNCTION(socket);
+ INTERCEPT_FUNCTION(send);
+ INTERCEPT_FUNCTION(sendmsg);
+ INTERCEPT_FUNCTION(sendto);
+ INTERCEPT_FUNCTION(recv);
+ INTERCEPT_FUNCTION(recvmsg);
+ INTERCEPT_FUNCTION(recvfrom);
+ INTERCEPT_FUNCTION(shutdown);
+}
diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors.h b/compiler-rt/lib/rtsan/rtsan_interceptors.h
new file mode 100644
index 0000000..a3dac27
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors.h
@@ -0,0 +1,15 @@
+//===--- rtsan_interceptors.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 {
+void InitializeInterceptors();
+} // namespace __rtsan
diff --git a/compiler-rt/lib/rtsan/rtsan_preinit.cpp b/compiler-rt/lib/rtsan/rtsan_preinit.cpp
new file mode 100644
index 0000000..8cea61c
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_preinit.cpp
@@ -0,0 +1,23 @@
+//===--- rtsan_preinit.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 "sanitizer_common/sanitizer_internal_defs.h"
+#include <rtsan/rtsan.h>
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+
+// The symbol is called __local_rtsan_preinit, because it's not intended to be
+// exported.
+// This code is linked into the main executable when -fsanitize=realtime is in
+// the link flags. It can only use exported interface functions.
+__attribute__((section(".preinit_array"),
+ used)) void (*__local_rtsan_preinit)(void) = __rtsan_init;
+
+#endif
diff --git a/compiler-rt/lib/rtsan/rtsan_stack.cpp b/compiler-rt/lib/rtsan/rtsan_stack.cpp
new file mode 100644
index 0000000..43fd5fb
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_stack.cpp
@@ -0,0 +1,50 @@
+//===--- rtsan_stack.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_stack.h"
+
+#include <sanitizer_common/sanitizer_flags.h>
+#include <sanitizer_common/sanitizer_stacktrace.h>
+
+using namespace __sanitizer;
+using namespace __rtsan;
+
+// We must define our own implementation of this method for our runtime.
+// This one is just copied from UBSan.
+namespace __sanitizer {
+void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context,
+ bool request_fast, u32 max_depth) {
+ uptr top = 0;
+ uptr bottom = 0;
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ bool fast = StackTrace::WillUseFastUnwind(request_fast);
+ Unwind(max_depth, pc, bp, context, top, bottom, fast);
+}
+} // namespace __sanitizer
+
+static void SetGlobalStackTraceFormat() {
+ SetCommonFlagsDefaults();
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.stack_trace_format = "DEFAULT";
+ cf.external_symbolizer_path = GetEnv("RTSAN_SYMBOLIZER_PATH");
+ OverrideCommonFlags(cf);
+}
+
+void __rtsan::PrintStackTrace() {
+
+ BufferedStackTrace stack{};
+
+ GET_CURRENT_PC_BP;
+ stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal);
+
+ SetGlobalStackTraceFormat();
+ stack.Print();
+}
diff --git a/compiler-rt/lib/rtsan/rtsan_stack.h b/compiler-rt/lib/rtsan/rtsan_stack.h
new file mode 100644
index 0000000..cecdd43
--- /dev/null
+++ b/compiler-rt/lib/rtsan/rtsan_stack.h
@@ -0,0 +1,15 @@
+//===--- rtsan_stack.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 {
+void PrintStackTrace();
+} // namespace __rtsan
diff --git a/compiler-rt/lib/rtsan/tests/CMakeLists.txt b/compiler-rt/lib/rtsan/tests/CMakeLists.txt
new file mode 100644
index 0000000..d96e538
--- /dev/null
+++ b/compiler-rt/lib/rtsan/tests/CMakeLists.txt
@@ -0,0 +1,103 @@
+include(CompilerRTCompile)
+
+include_directories(..)
+
+set(RTSAN_UNITTEST_CFLAGS
+ ${COMPILER_RT_UNITTEST_CFLAGS}
+ ${COMPILER_RT_GTEST_CFLAGS}
+ ${COMPILER_RT_GMOCK_CFLAGS}
+ ${SANITIZER_TEST_CXX_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/lib/
+ -I${COMPILER_RT_SOURCE_DIR}/include/
+ -I${COMPILER_RT_SOURCE_DIR}/lib/rtsan
+ -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/tests
+ -DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
+ -O2)
+
+set(RTSAN_INST_TEST_SOURCES
+ rtsan_test_functional.cpp
+ rtsan_test_interceptors.cpp
+ rtsan_test_main.cpp)
+
+set(RTSAN_NOINST_TEST_SOURCES
+ ../rtsan_preinit.cpp
+ rtsan_test_context.cpp
+ rtsan_test_main.cpp)
+
+set(RTSAN_UNITTEST_HEADERS
+ rtsan_test_utilities.h)
+
+add_custom_target(RtsanUnitTests)
+set_target_properties(RtsanUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
+
+set(RTSAN_UNITTEST_LINK_FLAGS
+ ${COMPILER_RT_UNITTEST_LINK_FLAGS}
+ ${COMPILER_RT_UNWINDER_LINK_LIBS}
+ ${SANITIZER_TEST_CXX_LIBRARIES}
+ -no-pie)
+
+if (APPLE)
+ add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
+ list(APPEND RTSAN_UNITTEST_LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS})
+ list(APPEND RTSAN_UNITTEST_LINK_FLAGS ${DARWIN_osx_LINK_FLAGS})
+ list(APPEND RTSAN_UNITTEST_CFLAGS ${DARWIN_osx_CFLAGS})
+else()
+ list(APPEND RTSAN_UNITTEST_LINK_FLAGS -latomic)
+endif()
+
+set(COMPILER_RT_GOOGLETEST_SOURCES ${COMPILER_RT_GTEST_SOURCE} ${COMPILER_RT_GMOCK_SOURCE})
+
+set(RTSAN_TEST_ARCH ${RTSAN_SUPPORTED_ARCH})
+if(APPLE)
+ darwin_filter_host_archs(RTSAN_SUPPORTED_ARCH RTSAN_TEST_ARCH)
+endif()
+
+foreach(arch ${RTSAN_TEST_ARCH})
+ set(RtsanTestObjects)
+
+ # TODO: Re-enable once -fsanitize=realtime exists in clang driver
+ #generate_compiler_rt_tests(RtsanTestObjects
+ # RtsanUnitTests "Rtsan-${arch}-Test" ${arch}
+ # COMPILE_DEPS ${RTSAN_UNITTEST_HEADERS}
+ # SOURCES ${RTSAN_INST_TEST_SOURCES} ${COMPILER_RT_GOOGLETEST_SOURCES}
+ # DEPS llvm_gtest rtsan
+ # CFLAGS ${RTSAN_UNITTEST_CFLAGS} -fsanitize=realtime
+ # LINK_FLAGS ${RTSAN_UNITTEST_LINK_FLAGS} -fsanitize=realtime)
+
+ set(RTSAN_TEST_RUNTIME RTRtsanTest.${arch})
+ if(APPLE)
+ set(RTSAN_TEST_RUNTIME_OBJECTS
+ $<TARGET_OBJECTS:RTRtsan.osx>
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommonCoverage.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.osx>)
+ else()
+ set(RTSAN_TEST_RUNTIME_OBJECTS
+ $<TARGET_OBJECTS:RTRtsan.${arch}>
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonCoverage.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonSymbolizerInternal.${arch}>)
+ endif()
+ add_library(${RTSAN_TEST_RUNTIME} STATIC ${RTSAN_TEST_RUNTIME_OBJECTS})
+ set_target_properties(${RTSAN_TEST_RUNTIME} PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ FOLDER "Compiler-RT Runtime tests")
+
+ set(RtsanNoInstTestObjects)
+ generate_compiler_rt_tests(RtsanNoInstTestObjects
+ RtsanUnitTests "Rtsan-${arch}-NoInstTest" ${arch}
+ COMPILE_DEPS ${RTSAN_UNITTEST_HEADERS}
+ SOURCES ${RTSAN_NOINST_TEST_SOURCES}
+ ${COMPILER_RT_GOOGLETEST_SOURCES}
+ DEPS llvm_gtest
+ CFLAGS ${RTSAN_UNITTEST_CFLAGS}
+ LINK_FLAGS ${RTSAN_UNITTEST_LINK_FLAGS}
+ RUNTIME ${RTSAN_TEST_RUNTIME})
+ set_target_properties(RtsanUnitTests PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endforeach()
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp
new file mode 100644
index 0000000..b7e7323
--- /dev/null
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_context.cpp
@@ -0,0 +1,69 @@
+//===--- rtsan_test_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_test_utilities.h"
+
+#include "rtsan_context.h"
+
+TEST(TestRtsanContext, CanCreateContext) { __rtsan::Context context{}; }
+
+TEST(TestRtsanContext, ExpectNotRealtimeDoesNotDieBeforeRealtimePush) {
+ __rtsan::Context context{};
+ context.ExpectNotRealtime("do_some_stuff");
+}
+
+TEST(TestRtsanContext, ExpectNotRealtimeDoesNotDieAfterPushAndPop) {
+ __rtsan::Context context{};
+ context.RealtimePush();
+ context.RealtimePop();
+ context.ExpectNotRealtime("do_some_stuff");
+}
+
+TEST(TestRtsanContext, ExpectNotRealtimeDiesAfterRealtimePush) {
+ __rtsan::Context context{};
+
+ context.RealtimePush();
+ EXPECT_DEATH(context.ExpectNotRealtime("do_some_stuff"), "");
+}
+
+TEST(TestRtsanContext,
+ ExpectNotRealtimeDiesAfterRealtimeAfterMorePushesThanPops) {
+ __rtsan::Context context{};
+
+ context.RealtimePush();
+ context.RealtimePush();
+ context.RealtimePush();
+ context.RealtimePop();
+ context.RealtimePop();
+ EXPECT_DEATH(context.ExpectNotRealtime("do_some_stuff"), "");
+}
+
+TEST(TestRtsanContext, ExpectNotRealtimeDoesNotDieAfterBypassPush) {
+ __rtsan::Context context{};
+
+ context.RealtimePush();
+ context.BypassPush();
+ context.ExpectNotRealtime("do_some_stuff");
+}
+
+TEST(TestRtsanContext,
+ ExpectNotRealtimeDoesNotDieIfBypassDepthIsGreaterThanZero) {
+ __rtsan::Context context{};
+
+ context.RealtimePush();
+ context.BypassPush();
+ context.BypassPush();
+ context.BypassPush();
+ context.BypassPop();
+ context.BypassPop();
+ context.ExpectNotRealtime("do_some_stuff");
+ context.BypassPop();
+ EXPECT_DEATH(context.ExpectNotRealtime("do_some_stuff"), "");
+}
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp
new file mode 100644
index 0000000..97afb1e
--- /dev/null
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp
@@ -0,0 +1,207 @@
+//===--- rtsan_test.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Introduces basic functional tests for the realtime sanitizer.
+// Not meant to be exhaustive, testing all interceptors, please see
+// test_rtsan_interceptors.cpp for those tests.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "rtsan_test_utilities.h"
+#include <rtsan.h>
+#include <sanitizer_common/sanitizer_platform.h>
+#include <sanitizer_common/sanitizer_platform_interceptors.h>
+
+#include <array>
+#include <atomic>
+#include <chrono>
+#include <fstream>
+#include <mutex>
+#include <shared_mutex>
+#include <thread>
+
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
+ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
+#define SI_MAC_DEPLOYMENT_AT_LEAST_10_12 1
+#else
+#define SI_MAC_DEPLOYMENT_AT_LEAST_10_12 0
+#endif
+
+#define RTSAN_TEST_SHARED_MUTEX (!(SI_MAC) || SI_MAC_DEPLOYMENT_AT_LEAST_10_12)
+
+using namespace testing;
+using namespace rtsan_testing;
+using namespace std::chrono_literals;
+
+TEST(TestRtsan, VectorPushBackAllocationDiesWhenRealtime) {
+ std::vector<float> vec;
+ auto Func = [&vec]() { vec.push_back(0.4f); };
+ ExpectRealtimeDeath(Func);
+ ASSERT_EQ(0u, vec.size());
+ ExpectNonRealtimeSurvival(Func);
+ ASSERT_EQ(1u, vec.size());
+}
+
+TEST(TestRtsan, DestructionOfObjectOnHeapDiesWhenRealtime) {
+ auto allocated_ptr = std::make_unique<std::array<float, 256>>();
+ auto Func = [&allocated_ptr]() { allocated_ptr.reset(); };
+ ExpectRealtimeDeath(Func);
+ ASSERT_NE(nullptr, allocated_ptr.get());
+ ExpectNonRealtimeSurvival(Func);
+ ASSERT_EQ(nullptr, allocated_ptr.get());
+}
+
+TEST(TestRtsan, SleepingAThreadDiesWhenRealtime) {
+ auto Func = []() { std::this_thread::sleep_for(1us); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, IfstreamCreationDiesWhenRealtime) {
+ auto Func = []() { std::ifstream ifs{"./file.txt"}; };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+ std::remove("./file.txt");
+}
+
+TEST(TestRtsan, OfstreamCreationDiesWhenRealtime) {
+ auto Func = []() { std::ofstream ofs{"./file.txt"}; };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+ std::remove("./file.txt");
+}
+
+TEST(TestRtsan, LockingAMutexDiesWhenRealtime) {
+ std::mutex mutex;
+ auto Func = [&]() { mutex.lock(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, UnlockingAMutexDiesWhenRealtime) {
+ std::mutex mutex;
+ mutex.lock();
+ auto Func = [&]() { mutex.unlock(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if RTSAN_TEST_SHARED_MUTEX
+
+TEST(TestRtsan, LockingASharedMutexDiesWhenRealtime) {
+ std::shared_mutex mutex;
+ auto Func = [&]() { mutex.lock(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, UnlockingASharedMutexDiesWhenRealtime) {
+ std::shared_mutex mutex;
+ mutex.lock();
+ auto Func = [&]() { mutex.unlock(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, SharedLockingASharedMutexDiesWhenRealtime) {
+ std::shared_mutex mutex;
+ auto Func = [&]() { mutex.lock_shared(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, SharedUnlockingASharedMutexDiesWhenRealtime) {
+ std::shared_mutex mutex;
+ mutex.lock_shared();
+ auto Func = [&]() { mutex.unlock_shared(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#endif // RTSAN_TEST_SHARED_MUTEX
+
+TEST(TestRtsan, LaunchingAThreadDiesWhenRealtime) {
+ auto Func = [&]() {
+ std::thread Thread{[]() {}};
+ Thread.join();
+ };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+namespace {
+void InvokeStdFunction(std::function<void()> &&function) { function(); }
+} // namespace
+
+TEST(TestRtsan, CopyingALambdaWithLargeCaptureDiesWhenRealtime) {
+ std::array<float, 16> lots_of_data;
+ auto lambda = [lots_of_data]() mutable {
+ // Stop everything getting optimised out
+ lots_of_data[3] = 0.25f;
+ EXPECT_EQ(16, lots_of_data.size());
+ EXPECT_EQ(0.25f, lots_of_data[3]);
+ };
+ auto Func = [&]() { InvokeStdFunction(lambda); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, AccessingALargeAtomicVariableDiesWhenRealtime) {
+ std::atomic<float> small_atomic{0.0f};
+ ASSERT_TRUE(small_atomic.is_lock_free());
+ RealtimeInvoke([&small_atomic]() { float x = small_atomic.load(); });
+
+ std::atomic<std::array<float, 2048>> large_atomic;
+ ASSERT_FALSE(large_atomic.is_lock_free());
+ auto Func = [&]() { auto x = large_atomic.load(); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, FirstCoutDiesWhenRealtime) {
+ auto Func = []() { std::cout << "Hello, world!" << std::endl; };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, SecondCoutDiesWhenRealtime) {
+ std::cout << "Hello, world";
+ auto Func = []() { std::cout << "Hello, again!" << std::endl; };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, PrintfDiesWhenRealtime) {
+ auto Func = []() { printf("Hello, world!\n"); };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, ThrowingAnExceptionDiesWhenRealtime) {
+ auto Func = [&]() {
+ try {
+ throw std::exception();
+ } catch (std::exception &) {
+ }
+ };
+ ExpectRealtimeDeath(Func);
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsan, DoesNotDieIfTurnedOff) {
+ std::mutex mutex;
+ auto RealtimeUnsafeFunc = [&]() {
+ __rtsan_off();
+ mutex.lock();
+ mutex.unlock();
+ __rtsan_on();
+ };
+ RealtimeInvoke(RealtimeUnsafeFunc);
+}
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp
new file mode 100644
index 0000000..f5b0160
--- /dev/null
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors.cpp
@@ -0,0 +1,516 @@
+//===--- rtsan_test_interceptors.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 "gtest/gtest.h"
+
+#include <sanitizer_common/sanitizer_platform.h>
+#include <sanitizer_common/sanitizer_platform_interceptors.h>
+
+#include "rtsan_test_utilities.h"
+
+#if SANITIZER_APPLE
+#include <libkern/OSAtomic.h>
+#include <os/lock.h>
+#endif
+
+#if SANITIZER_INTERCEPT_MEMALIGN || SANITIZER_INTERCEPT_PVALLOC
+#include <malloc.h>
+#endif
+
+#include <atomic>
+#include <chrono>
+#include <string>
+#include <thread>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+using namespace testing;
+using namespace rtsan_testing;
+using namespace std::chrono_literals;
+
+void *FakeThreadEntryPoint(void *) { return nullptr; }
+
+class RtsanFileTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ const ::testing::TestInfo *const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ file_path_ = std::string("/tmp/rtsan_temporary_test_file_") +
+ test_info->name() + ".txt";
+ RemoveTemporaryFile();
+ }
+
+ // Gets a file path with the test's name in it
+ // This file will be removed if it exists at the end of the test
+ const char *GetTemporaryFilePath() const { return file_path_.c_str(); }
+
+ void TearDown() override { RemoveTemporaryFile(); }
+
+private:
+ void RemoveTemporaryFile() const { std::remove(GetTemporaryFilePath()); }
+ std::string file_path_;
+};
+
+/*
+ Allocation and deallocation
+*/
+
+TEST(TestRtsanInterceptors, MallocDiesWhenRealtime) {
+ auto Func = []() { EXPECT_NE(nullptr, malloc(1)); };
+ ExpectRealtimeDeath(Func, "malloc");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, ReallocDiesWhenRealtime) {
+ void *ptr_1 = malloc(1);
+ auto Func = [ptr_1]() { EXPECT_NE(nullptr, realloc(ptr_1, 8)); };
+ ExpectRealtimeDeath(Func, "realloc");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if SANITIZER_APPLE
+TEST(TestRtsanInterceptors, ReallocfDiesWhenRealtime) {
+ void *ptr_1 = malloc(1);
+ auto Func = [ptr_1]() { EXPECT_NE(nullptr, reallocf(ptr_1, 8)); };
+ ExpectRealtimeDeath(Func, "reallocf");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+TEST(TestRtsanInterceptors, VallocDiesWhenRealtime) {
+ auto Func = []() { EXPECT_NE(nullptr, valloc(4)); };
+ ExpectRealtimeDeath(Func, "valloc");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
+TEST(TestRtsanInterceptors, AlignedAllocDiesWhenRealtime) {
+ auto Func = []() { EXPECT_NE(nullptr, aligned_alloc(16, 32)); };
+ ExpectRealtimeDeath(Func, "aligned_alloc");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+// free_sized and free_aligned_sized (both C23) are not yet supported
+TEST(TestRtsanInterceptors, FreeDiesWhenRealtime) {
+ void *ptr_1 = malloc(1);
+ void *ptr_2 = malloc(1);
+ ExpectRealtimeDeath([ptr_1]() { free(ptr_1); }, "free");
+ ExpectNonRealtimeSurvival([ptr_2]() { free(ptr_2); });
+
+ // Prevent malloc/free pair being optimised out
+ ASSERT_NE(nullptr, ptr_1);
+ ASSERT_NE(nullptr, ptr_2);
+}
+
+TEST(TestRtsanInterceptors, FreeSurvivesWhenRealtimeIfArgumentIsNull) {
+ RealtimeInvoke([]() { free(NULL); });
+ ExpectNonRealtimeSurvival([]() { free(NULL); });
+}
+
+TEST(TestRtsanInterceptors, PosixMemalignDiesWhenRealtime) {
+ auto Func = []() {
+ void *ptr;
+ posix_memalign(&ptr, 4, 4);
+ };
+ ExpectRealtimeDeath(Func, "posix_memalign");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if SANITIZER_INTERCEPT_MEMALIGN
+TEST(TestRtsanInterceptors, MemalignDiesWhenRealtime) {
+ auto Func = []() { EXPECT_NE(memalign(2, 2048), nullptr); };
+ ExpectRealtimeDeath(Func, "memalign");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_PVALLOC
+TEST(TestRtsanInterceptors, PvallocDiesWhenRealtime) {
+ auto Func = []() { EXPECT_NE(pvalloc(2048), nullptr); };
+ ExpectRealtimeDeath(Func, "pvalloc");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+/*
+ Sleeping
+*/
+
+TEST(TestRtsanInterceptors, SleepDiesWhenRealtime) {
+ auto Func = []() { sleep(0u); };
+ ExpectRealtimeDeath(Func, "sleep");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, UsleepDiesWhenRealtime) {
+ auto Func = []() { usleep(1u); };
+ ExpectRealtimeDeath(Func, "usleep");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, NanosleepDiesWhenRealtime) {
+ auto Func = []() {
+ timespec T{};
+ nanosleep(&T, &T);
+ };
+ ExpectRealtimeDeath(Func, "nanosleep");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+/*
+ Filesystem
+*/
+
+TEST_F(RtsanFileTest, OpenDiesWhenRealtime) {
+ auto func = [this]() { open(GetTemporaryFilePath(), O_RDONLY); };
+ ExpectRealtimeDeath(func, "open");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, OpenatDiesWhenRealtime) {
+ auto func = [this]() { openat(0, GetTemporaryFilePath(), O_RDONLY); };
+ ExpectRealtimeDeath(func, "openat");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, OpenCreatesFileWithProperMode) {
+ const int mode = S_IRGRP | S_IROTH | S_IRUSR | S_IWUSR;
+
+ const int fd = open(GetTemporaryFilePath(), O_CREAT | O_WRONLY, mode);
+ ASSERT_THAT(fd, Ne(-1));
+ close(fd);
+
+ struct stat st;
+ ASSERT_THAT(stat(GetTemporaryFilePath(), &st), Eq(0));
+
+ // Mask st_mode to get permission bits only
+ ASSERT_THAT(st.st_mode & 0777, Eq(mode));
+}
+
+TEST_F(RtsanFileTest, CreatDiesWhenRealtime) {
+ auto func = [this]() { creat(GetTemporaryFilePath(), S_IWOTH | S_IROTH); };
+ ExpectRealtimeDeath(func, "creat");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST(TestRtsanInterceptors, FcntlDiesWhenRealtime) {
+ auto func = []() { fcntl(0, F_GETFL); };
+ ExpectRealtimeDeath(func, "fcntl");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, FcntlFlockDiesWhenRealtime) {
+ int fd = creat(GetTemporaryFilePath(), S_IRUSR | S_IWUSR);
+ ASSERT_THAT(fd, Ne(-1));
+
+ auto func = [fd]() {
+ struct flock lock {};
+ lock.l_type = F_RDLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = ::getpid();
+
+ ASSERT_THAT(fcntl(fd, F_GETLK, &lock), Eq(0));
+ ASSERT_THAT(lock.l_type, F_UNLCK);
+ };
+ ExpectRealtimeDeath(func, "fcntl");
+ ExpectNonRealtimeSurvival(func);
+
+ close(fd);
+}
+
+TEST_F(RtsanFileTest, FcntlSetFdDiesWhenRealtime) {
+ int fd = creat(GetTemporaryFilePath(), S_IRUSR | S_IWUSR);
+ ASSERT_THAT(fd, Ne(-1));
+
+ auto func = [fd]() {
+ int old_flags = fcntl(fd, F_GETFD);
+ ASSERT_THAT(fcntl(fd, F_SETFD, FD_CLOEXEC), Eq(0));
+
+ int flags = fcntl(fd, F_GETFD);
+ ASSERT_THAT(flags, Ne(-1));
+ ASSERT_THAT(flags & FD_CLOEXEC, Eq(FD_CLOEXEC));
+
+ ASSERT_THAT(fcntl(fd, F_SETFD, old_flags), Eq(0));
+ ASSERT_THAT(fcntl(fd, F_GETFD), Eq(old_flags));
+ };
+
+ ExpectRealtimeDeath(func, "fcntl");
+ ExpectNonRealtimeSurvival(func);
+
+ close(fd);
+}
+
+TEST(TestRtsanInterceptors, CloseDiesWhenRealtime) {
+ auto func = []() { close(0); };
+ ExpectRealtimeDeath(func, "close");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, FopenDiesWhenRealtime) {
+ auto func = [this]() {
+ auto fd = fopen(GetTemporaryFilePath(), "w");
+ EXPECT_THAT(fd, Ne(nullptr));
+ };
+ ExpectRealtimeDeath(func, "fopen");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, FreadDiesWhenRealtime) {
+ auto fd = fopen(GetTemporaryFilePath(), "w");
+ auto func = [fd]() {
+ char c{};
+ fread(&c, 1, 1, fd);
+ };
+ ExpectRealtimeDeath(func, "fread");
+ ExpectNonRealtimeSurvival(func);
+ if (fd != nullptr)
+ fclose(fd);
+}
+
+TEST_F(RtsanFileTest, FwriteDiesWhenRealtime) {
+ auto fd = fopen(GetTemporaryFilePath(), "w");
+ ASSERT_NE(nullptr, fd);
+ auto message = "Hello, world!";
+ auto func = [&]() { fwrite(&message, 1, 4, fd); };
+ ExpectRealtimeDeath(func, "fwrite");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, FcloseDiesWhenRealtime) {
+ auto fd = fopen(GetTemporaryFilePath(), "w");
+ EXPECT_THAT(fd, Ne(nullptr));
+ auto func = [fd]() { fclose(fd); };
+ ExpectRealtimeDeath(func, "fclose");
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST(TestRtsanInterceptors, PutsDiesWhenRealtime) {
+ auto func = []() { puts("Hello, world!\n"); };
+ ExpectRealtimeDeath(func);
+ ExpectNonRealtimeSurvival(func);
+}
+
+TEST_F(RtsanFileTest, FputsDiesWhenRealtime) {
+ auto fd = fopen(GetTemporaryFilePath(), "w");
+ ASSERT_THAT(fd, Ne(nullptr)) << errno;
+ auto func = [fd]() { fputs("Hello, world!\n", fd); };
+ ExpectRealtimeDeath(func);
+ ExpectNonRealtimeSurvival(func);
+ if (fd != nullptr)
+ fclose(fd);
+}
+
+/*
+ Concurrency
+*/
+
+TEST(TestRtsanInterceptors, PthreadCreateDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_t thread{};
+ const pthread_attr_t attr{};
+ struct thread_info *thread_info;
+ pthread_create(&thread, &attr, &FakeThreadEntryPoint, thread_info);
+ };
+ ExpectRealtimeDeath(Func, "pthread_create");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, PthreadMutexLockDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_mutex_t mutex{};
+ pthread_mutex_lock(&mutex);
+ };
+
+ ExpectRealtimeDeath(Func, "pthread_mutex_lock");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, PthreadMutexUnlockDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_mutex_t mutex{};
+ pthread_mutex_unlock(&mutex);
+ };
+
+ ExpectRealtimeDeath(Func, "pthread_mutex_unlock");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, PthreadMutexJoinDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_t thread{};
+ pthread_join(thread, nullptr);
+ };
+
+ ExpectRealtimeDeath(Func, "pthread_join");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+#if SANITIZER_APPLE
+
+#pragma clang diagnostic push
+// OSSpinLockLock is deprecated, but still in use in libc++
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+TEST(TestRtsanInterceptors, OsSpinLockLockDiesWhenRealtime) {
+ auto Func = []() {
+ OSSpinLock spin_lock{};
+ OSSpinLockLock(&spin_lock);
+ };
+ ExpectRealtimeDeath(Func, "OSSpinLockLock");
+ ExpectNonRealtimeSurvival(Func);
+}
+#pragma clang diagnostic pop
+
+TEST(TestRtsanInterceptors, OsUnfairLockLockDiesWhenRealtime) {
+ auto Func = []() {
+ os_unfair_lock_s unfair_lock{};
+ os_unfair_lock_lock(&unfair_lock);
+ };
+ ExpectRealtimeDeath(Func, "os_unfair_lock_lock");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+#if SANITIZER_LINUX
+TEST(TestRtsanInterceptors, SpinLockLockDiesWhenRealtime) {
+ pthread_spinlock_t spin_lock;
+ pthread_spin_init(&spin_lock, PTHREAD_PROCESS_SHARED);
+ auto Func = [&]() { pthread_spin_lock(&spin_lock); };
+ ExpectRealtimeDeath(Func, "pthread_spin_lock");
+ ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
+TEST(TestRtsanInterceptors, PthreadCondSignalDiesWhenRealtime) {
+ pthread_cond_t cond{};
+ pthread_cond_init(&cond, NULL);
+
+ auto Func = [&cond]() { pthread_cond_signal(&cond); };
+ ExpectRealtimeDeath(Func, "pthread_cond_signal");
+ ExpectNonRealtimeSurvival(Func);
+
+ pthread_cond_destroy(&cond);
+}
+
+TEST(TestRtsanInterceptors, PthreadCondBroadcastDiesWhenRealtime) {
+ pthread_cond_t cond{};
+ pthread_cond_init(&cond, NULL);
+
+ auto Func = [&cond]() { pthread_cond_broadcast(&cond); };
+ ExpectRealtimeDeath(Func, "pthread_cond_broadcast");
+ ExpectNonRealtimeSurvival(Func);
+
+ pthread_cond_destroy(&cond);
+}
+
+TEST(TestRtsanInterceptors, PthreadCondWaitDiesWhenRealtime) {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ ASSERT_EQ(0, pthread_cond_init(&cond, nullptr));
+ ASSERT_EQ(0, pthread_mutex_init(&mutex, nullptr));
+
+ auto Func = [&]() { pthread_cond_wait(&cond, &mutex); };
+ ExpectRealtimeDeath(Func, "pthread_cond_wait");
+ // It's very difficult to test the success case here without doing some
+ // sleeping, which is at the mercy of the scheduler. What's really important
+ // here is the interception - so we're only testing that for now.
+
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mutex);
+}
+
+TEST(TestRtsanInterceptors, PthreadRwlockRdlockDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_rwlock_t rw_lock;
+ pthread_rwlock_rdlock(&rw_lock);
+ };
+ ExpectRealtimeDeath(Func, "pthread_rwlock_rdlock");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, PthreadRwlockUnlockDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_rwlock_t rw_lock;
+ pthread_rwlock_unlock(&rw_lock);
+ };
+ ExpectRealtimeDeath(Func, "pthread_rwlock_unlock");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, PthreadRwlockWrlockDiesWhenRealtime) {
+ auto Func = []() {
+ pthread_rwlock_t rw_lock;
+ pthread_rwlock_wrlock(&rw_lock);
+ };
+ ExpectRealtimeDeath(Func, "pthread_rwlock_wrlock");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+/*
+ Sockets
+*/
+TEST(TestRtsanInterceptors, OpeningASocketDiesWhenRealtime) {
+ auto Func = []() { socket(PF_INET, SOCK_STREAM, 0); };
+ ExpectRealtimeDeath(Func, "socket");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, SendToASocketDiesWhenRealtime) {
+ auto Func = []() { send(0, nullptr, 0, 0); };
+ ExpectRealtimeDeath(Func, "send");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, SendmsgToASocketDiesWhenRealtime) {
+ msghdr msg{};
+ auto Func = [&]() { sendmsg(0, &msg, 0); };
+ ExpectRealtimeDeath(Func, "sendmsg");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, SendtoToASocketDiesWhenRealtime) {
+ sockaddr addr{};
+ socklen_t len{};
+ auto Func = [&]() { sendto(0, nullptr, 0, 0, &addr, len); };
+ ExpectRealtimeDeath(Func, "sendto");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, RecvFromASocketDiesWhenRealtime) {
+ auto Func = []() { recv(0, nullptr, 0, 0); };
+ ExpectRealtimeDeath(Func, "recv");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, RecvfromOnASocketDiesWhenRealtime) {
+ sockaddr addr{};
+ socklen_t len{};
+ auto Func = [&]() { recvfrom(0, nullptr, 0, 0, &addr, &len); };
+ ExpectRealtimeDeath(Func, "recvfrom");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, RecvmsgOnASocketDiesWhenRealtime) {
+ msghdr msg{};
+ auto Func = [&]() { recvmsg(0, &msg, 0); };
+ ExpectRealtimeDeath(Func, "recvmsg");
+ ExpectNonRealtimeSurvival(Func);
+}
+
+TEST(TestRtsanInterceptors, ShutdownOnASocketDiesWhenRealtime) {
+ auto Func = [&]() { shutdown(0, 0); };
+ ExpectRealtimeDeath(Func, "shutdown");
+ ExpectNonRealtimeSurvival(Func);
+}
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp
new file mode 100644
index 0000000..255ac94
--- /dev/null
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_main.cpp
@@ -0,0 +1,17 @@
+//===--- rtsan_test_main.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 "sanitizer_test_utils.h"
+
+int main(int argc, char **argv) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h b/compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h
new file mode 100644
index 0000000..6ca09cf
--- /dev/null
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_utilities.h
@@ -0,0 +1,47 @@
+//===--- rtsan_test_utilities.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 "rtsan.h"
+#include "gmock/gmock.h"
+#include <string>
+
+namespace rtsan_testing {
+
+template <typename Function> void RealtimeInvoke(Function &&Func) {
+ __rtsan_realtime_enter();
+ std::forward<Function>(Func)();
+ __rtsan_realtime_exit();
+}
+
+template <typename Function>
+void ExpectRealtimeDeath(Function &&Func,
+ const char *intercepted_method_name = nullptr) {
+
+ using namespace testing;
+
+ auto GetExpectedErrorSubstring = [&]() -> std::string {
+ return intercepted_method_name != nullptr
+ ? "Real-time violation: intercepted call to real-time unsafe "
+ "function `" +
+ std::string(intercepted_method_name) + "`"
+ : "";
+ };
+
+ EXPECT_EXIT(RealtimeInvoke(std::forward<Function>(Func)),
+ ExitedWithCode(EXIT_FAILURE), GetExpectedErrorSubstring());
+}
+
+template <typename Function> void ExpectNonRealtimeSurvival(Function &&Func) {
+ std::forward<Function>(Func)();
+}
+
+} // namespace rtsan_testing
diff --git a/compiler-rt/test/rtsan/CMakeLists.txt b/compiler-rt/test/rtsan/CMakeLists.txt
new file mode 100644
index 0000000..ba10704
--- /dev/null
+++ b/compiler-rt/test/rtsan/CMakeLists.txt
@@ -0,0 +1,58 @@
+
+
+
+
+######
+# TODO: Full lit tests coming in a future review when we introduce the codegen
+######
+
+
+
+
+set(RTSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+set(RTSAN_TESTSUITES)
+set(RTSAN_FDR_TESTSUITES)
+
+set(RTSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
+set(RTSAN_FDR_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
+set(RTSAN_TEST_ARCH ${RTSAN_SUPPORTED_ARCH})
+if(APPLE)
+ darwin_filter_host_archs(RTSAN_SUPPORTED_ARCH RTSAN_TEST_ARCH)
+endif()
+
+if (COMPILER_RT_HAS_RTSAN)
+ foreach(arch ${RTSAN_TEST_ARCH})
+ set(RTSAN_TEST_TARGET_ARCH ${arch})
+ string(TOLOWER "-${arch}-${OS_NAME}" RTSAN_TEST_CONFIG_SUFFIX)
+ get_test_cc_for_arch(${arch} RTSAN_TEST_TARGET_CC RTSAN_TEST_TARGET_CFLAGS)
+ string(TOUPPER ${arch} ARCH_UPPER_CASE)
+ set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config)
+
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
+ ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py)
+ list(APPEND RTSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+ endforeach()
+endif()
+
+if(COMPILER_RT_INCLUDE_TESTS)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.py.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg.py)
+ if(COMPILER_RT_RTSAN_HAS_STATIC_RUNTIME)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.py.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/dynamic/lit.site.cfg.py)
+ endif()
+ list(APPEND RTSAN_TEST_DEPS RtsanUnitTests)
+ list(APPEND RTSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit)
+ if(COMPILER_RT_RTSAN_HAS_STATIC_RUNTIME)
+ list(APPEND RTSAN_DYNAMIC_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit/dynamic)
+ endif()
+endif()
+
+add_lit_testsuite(check-rtsan "Running the Rtsan tests"
+ ${RTSAN_TESTSUITES}
+ DEPENDS ${RTSAN_TEST_DEPS})
+set_target_properties(check-rtsan PROPERTIES FOLDER "Compiler-RT Misc")
diff --git a/compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in b/compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in
new file mode 100644
index 0000000..59e1e10
--- /dev/null
+++ b/compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in
@@ -0,0 +1,25 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+# Load common config for all compiler-rt unit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured")
+
+# Setup config name.
+config.name = 'RealtimeSanitizer-Unit'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with ASan unit tests.
+# FIXME: De-hardcode this path.
+config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/rtsan/tests"
+config.test_source_root = config.test_exec_root
+
+if not config.parallelism_group:
+ config.parallelism_group = 'shadow-memory'
+
+if config.host_os == 'Darwin':
+ # On Darwin, we default to ignore_noninstrumented_modules=1, which also
+ # suppresses some races the tests are supposed to find. See rtsan/lit.cfg.py.
+ if 'RTSAN_OPTIONS' in config.environment:
+ config.environment['RTSAN_OPTIONS'] += ':ignore_noninstrumented_modules=0'
+ else:
+ config.environment['RTSAN_OPTIONS'] = 'ignore_noninstrumented_modules=0'
+ config.environment['RTSAN_OPTIONS'] += ':ignore_interceptors_accesses=0'
diff --git a/compiler-rt/test/rtsan/lit.cfg.py b/compiler-rt/test/rtsan/lit.cfg.py
new file mode 100644
index 0000000..b262ecf
--- /dev/null
+++ b/compiler-rt/test/rtsan/lit.cfg.py
@@ -0,0 +1,49 @@
+import os
+
+# Setup config name.
+config.name = "RTSAN" + config.name_suffix
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Setup default compiler flags use with -frtsan-instrument option.
+clang_rtsan_cflags = ["-frtsan-instrument", config.target_cflags]
+
+clang_rtsan_cxxflags = config.cxx_mode_flags + clang_rtsan_cflags
+
+
+def build_invocation(compile_flags):
+ return " " + " ".join([config.clang] + compile_flags) + " "
+
+
+# Assume that llvm-rtsan is in the config.llvm_tools_dir.
+llvm_rtsan = os.path.join(config.llvm_tools_dir, "llvm-rtsan")
+
+# Setup substitutions.
+if config.host_os == "Linux":
+ libdl_flag = "-ldl"
+else:
+ libdl_flag = ""
+
+config.substitutions.append(("%clang ", build_invocation([config.target_cflags])))
+config.substitutions.append(
+ ("%clangxx ", build_invocation(config.cxx_mode_flags + [config.target_cflags]))
+)
+config.substitutions.append(("%clang_rtsan ", build_invocation(clang_rtsan_cflags)))
+config.substitutions.append(("%clangxx_rtsan", build_invocation(clang_rtsan_cxxflags)))
+config.substitutions.append(("%llvm_rtsan", llvm_rtsan))
+
+# Default test suffixes.
+config.suffixes = [".c", ".cpp"]
+
+if config.host_os not in ["Darwin", "FreeBSD", "Linux", "NetBSD", "OpenBSD"]:
+ config.unsupported = True
+elif "64" not in config.host_arch:
+ if "arm" in config.host_arch:
+ if "-mthumb" in config.target_cflags:
+ config.unsupported = True
+ else:
+ config.unsupported = True
+
+if config.host_os == "NetBSD":
+ config.substitutions.insert(0, ("%run", config.netbsd_nomprotect_prefix))
diff --git a/compiler-rt/test/rtsan/lit.site.cfg.py.in b/compiler-rt/test/rtsan/lit.site.cfg.py.in
new file mode 100644
index 0000000..5b45864
--- /dev/null
+++ b/compiler-rt/test/rtsan/lit.site.cfg.py.in
@@ -0,0 +1,17 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+# Tool-specific config options.
+config.name_suffix = "@RTSAN_TEST_CONFIG_SUFFIX@"
+config.rtsan_lit_source_dir = "@RTSAN_LIT_SOURCE_DIR@"
+config.target_cflags = "@RTSAN_TEST_TARGET_CFLAGS@"
+config.target_arch = "@RTSAN_TEST_TARGET_ARCH@"
+config.built_with_llvm = ("@COMPILER_RT_STANDALONE_BUILD@" != "TRUE")
+
+if config.built_with_llvm:
+ config.available_features.add('built-in-llvm-tree')
+
+# Load common config for all compiler-rt lit tests
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg.py")