diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ede921c538096..3557a48cc12dd 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1294,15 +1294,6 @@ def Const : InheritableAttr { let SimpleHandler = 1; } -def Realtime : InheritableAttr { - let Spellings = [CXX11<"clang", "realtime">, - C23<"clang", "realtime">, - GCC<"realtime">]; - let Subjects = SubjectList<[Function]>; - let Documentation = [Undocumented]; - let SimpleHandler = 1; -} - def ConstInit : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index d442f25bac726..ab4bd003541fa 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -7652,13 +7652,6 @@ its underlying representation to be a WebAssembly ``funcref``. }]; } -def RealtimeDocs : Documentation { - let Category = DocCatFunction; - let Heading = "Realtime Attribute"; - let Content = [{ - }]; -} - def PreferredTypeDocumentation : Documentation { let Category = DocCatField; let Content = [{ diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 53f410d3cb4bd..95064eeccf091 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -103,6 +103,7 @@ FEATURE(memory_sanitizer, FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread)) FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow)) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) +FEATURE(realtime_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Realtime)) FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index f059c8715e26d..6c0df926aec5b 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -79,7 +79,7 @@ class SanitizerArgs { bool needsStableAbi() const { return StableABI; } bool needsMemProfRt() const { return NeedsMemProfRt; } - bool needsRadsanRt() const { return Sanitizers.has(SanitizerKind::Realtime); } + bool needsRtsanRt() const { return Sanitizers.has(SanitizerKind::Realtime); } bool needsAsanRt() const { return Sanitizers.has(SanitizerKind::Address); } bool needsHwasanRt() const { return Sanitizers.has(SanitizerKind::HWAddress); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 6d0304fe9f294..f29477c1608b6 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2385,8 +2385,6 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, FuncAttrs.addAttribute(llvm::Attribute::NoDuplicate); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::Convergent); - if (TargetDecl->hasAttr()) - FuncAttrs.addAttribute(llvm::Attribute::Realtime); if (const FunctionDecl *Fn = dyn_cast(TargetDecl)) { AddAttributesFromFunctionProtoType( @@ -2407,6 +2405,12 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, FuncAttrs.addAttribute(llvm::Attribute::NoReturn); NBA = Fn->getAttr(); } + + for (const FunctionEffectWithCondition& Fe : Fn->getFunctionEffects()) { + if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) { + FuncAttrs.addAttribute(llvm::Attribute::NonBlocking); + } + } } if (isa(TargetDecl) || isa(TargetDecl)) { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index b45077e86f293..899f9e2d451f0 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1615,7 +1615,7 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, insertCallAtFunctionEntryPoint(Fn, "radsan_off"); } - if (Fn->hasFnAttribute(llvm::Attribute::Realtime)) { + if (Fn->hasFnAttribute(llvm::Attribute::NonBlocking)) { insertCallAtFunctionEntryPoint(Fn, "radsan_realtime_enter"); } } @@ -1628,7 +1628,7 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, insertCallAtAllFunctionExitPoints(Fn, "radsan_on"); } - if (Fn->hasFnAttribute(llvm::Attribute::Realtime)) { + if (Fn->hasFnAttribute(llvm::Attribute::NonBlocking)) { insertCallAtAllFunctionExitPoints(Fn, "radsan_realtime_exit"); } } diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 86825a6ccf7a1..543b7b315423e 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -552,11 +552,14 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, SanitizerKind::Leak | SanitizerKind::Thread | SanitizerKind::Memory | SanitizerKind::KernelAddress | SanitizerKind::Scudo | SanitizerKind::SafeStack), - std::make_pair(SanitizerKind::MemTag, - SanitizerKind::Address | SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | - SanitizerKind::KernelHWAddress), - std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function)}; + std::make_pair(SanitizerKind::MemTag, SanitizerKind::Address | + SanitizerKind::KernelAddress | + SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress), + std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function), + std::make_pair(SanitizerKind::Realtime, + SanitizerKind::Address | SanitizerKind::Thread | + SanitizerKind::Undefined | SanitizerKind::Memory)}; // Enable toolchain specific default sanitizers if not explicitly disabled. SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index e56091119ac12..c75e5a84b005d 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1439,8 +1439,6 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, if (!Args.hasArg(options::OPT_shared)) HelperStaticRuntimes.push_back("hwasan-preinit"); } - if (SanArgs.needsRadsanRt() && SanArgs.linkRuntimes()) - SharedRuntimes.push_back("radsan"); } // The stats_client library is also statically linked into DSOs. @@ -1466,9 +1464,8 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, StaticRuntimes.push_back("asan_cxx"); } - if (!SanArgs.needsSharedRt() && SanArgs.needsRadsanRt() && SanArgs.linkRuntimes()) { - StaticRuntimes.push_back("radsan"); - } + if (!SanArgs.needsSharedRt() && SanArgs.needsRtsanRt()) + StaticRuntimes.push_back("rtsan"); if (!SanArgs.needsSharedRt() && SanArgs.needsMemProfRt()) { StaticRuntimes.push_back("memprof"); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index b75e6580890c1..0c1c13edfdd45 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1496,7 +1496,7 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, const char *sanitizer = nullptr; if (Sanitize.needsUbsanRt()) { sanitizer = "UndefinedBehaviorSanitizer"; - } else if (Sanitize.needsRadsanRt()) { + } else if (Sanitize.needsRtsanRt()) { sanitizer = "RealtimeSanitizer"; } else if (Sanitize.needsAsanRt()) { sanitizer = "AddressSanitizer"; @@ -1520,10 +1520,10 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, AddLinkSanitizerLibArgs(Args, CmdArgs, "asan"); } } - if(Sanitize.needsRadsanRt()) - { - assert(Sanitize.needsSharedRt() && "Static sanitizer runtimes not supported"); - AddLinkSanitizerLibArgs(Args, CmdArgs, "radsan"); + if (Sanitize.needsRtsanRt()) { + assert(Sanitize.needsSharedRt() && + "Static sanitizer runtimes not supported"); + AddLinkSanitizerLibArgs(Args, CmdArgs, "rtsan"); } if (Sanitize.needsLsanRt()) AddLinkSanitizerLibArgs(Args, CmdArgs, "lsan"); diff --git a/clang/runtime/CMakeLists.txt b/clang/runtime/CMakeLists.txt index c56bff11d476b..109de3d737548 100644 --- a/clang/runtime/CMakeLists.txt +++ b/clang/runtime/CMakeLists.txt @@ -150,7 +150,7 @@ if(LLVM_BUILD_EXTERNAL_COMPILER_RT AND EXISTS ${COMPILER_RT_SRC_ROOT}/) check-lsan check-msan check-profile - check-radsan + check-rtsan check-safestack check-sanitizer check-tsan diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index db14f6e195c64..d1a3ea9a37066 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -1038,3 +1038,50 @@ // RUN: not %clang --target=aarch64-none-elf -fsanitize=dataflow %s -### 2>&1 | FileCheck %s -check-prefix=UNSUPPORTED-BAREMETAL // RUN: not %clang --target=arm-arm-none-eabi -fsanitize=shadow-call-stack %s -### 2>&1 | FileCheck %s -check-prefix=UNSUPPORTED-BAREMETAL // UNSUPPORTED-BAREMETAL: unsupported option '-fsanitize={{.*}}' for target + +// RUN: %clang --target=x86_64-apple-darwin -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-DARWIN +// CHECK-RTSAN-X86-64-DARWIN-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-darwin -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-DARWIN +// CHECK-RTSAN-X86-64-DARWIN-NOT: unsupported option +// RUN: %clang --target=x86_64-apple-macos -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-MACOS +// CHECK-RTSAN-X86-64-MACOS-NOT: unsupported option +// RUN: %clang --target=arm64-apple-macos -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-MACOS +// CHECK-RTSAN-ARM64-MACOS-NOT: unsupported option + +// RUN: %clang --target=arm64-apple-ios-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-IOSSIMULATOR +// CHECK-RTSAN-ARM64-IOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=arm64-apple-watchos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-WATCHOSSIMULATOR +// CHECK-RTSAN-ARM64-WATCHOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=arm64-apple-tvos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-TVOSSIMULATOR +// CHECK-RTSAN-ARM64-TVOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-ios-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-IOSSIMULATOR +// CHECK-RTSAN-X86-64-IOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-watchos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-WATCHOSSIMULATOR +// CHECK-RTSAN-X86-64-WATCHOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-tvos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-TVOSSIMULATOR +// CHECK-RTSAN-X86-64-TVOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-LINUX +// CHECK-RTSAN-X86-64-LINUX-NOT: unsupported option + +// RUN: not %clang --target=i386-pc-openbsd -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-OPENBSD +// CHECK-RTSAN-OPENBSD: unsupported option '-fsanitize=realtime' for target 'i386-pc-openbsd' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-TSAN +// CHECK-REALTIME-TSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=thread' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-ASAN +// CHECK-REALTIME-ASAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=address' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-MSAN +// CHECK-REALTIME-MSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=memory' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-UBSAN +// CHECK-REALTIME-UBSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=undefined' + diff --git a/compiler-rt/CODE_OWNERS.TXT b/compiler-rt/CODE_OWNERS.TXT index ad136edf96781..570ab86508060 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 56a3a2590b999..bc152e304aaaf 100644 --- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake +++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake @@ -32,7 +32,7 @@ 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_RADSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64} +set(ALL_RTSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON} ${LOONGARCH64}) diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index 6d75cd9cdf23a..1130f9f721df4 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -462,7 +462,7 @@ if(APPLE) set(MIN_OSX_VERSION ${DEFAULT_SANITIZER_MIN_OSX_VERSION}) endif() -# Note: In order to target x86_64h on OS X the minimum deployment target must + # Note: In order to target x86_64h on OS X the minimum deployment target must # be 10.8 or higher. if(MIN_OSX_VERSION VERSION_LESS "10.7") message(FATAL_ERROR "macOS deployment target '${SANITIZER_MIN_OSX_VERSION}' is too old.") @@ -597,8 +597,8 @@ if(APPLE) list_intersect(ASAN_SUPPORTED_ARCH ALL_ASAN_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) - list_intersect(RADSAN_SUPPORTED_ARCH - ALL_RADSAN_SUPPORTED_ARCH + list_intersect(RTSAN_SUPPORTED_ARCH + ALL_RTSAN_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(DFSAN_SUPPORTED_ARCH ALL_DFSAN_SUPPORTED_ARCH @@ -666,7 +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(RADSAN_SUPPORTED_ARCH ${ALL_RADSAN_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}) @@ -720,7 +720,7 @@ if(COMPILER_RT_SUPPORTED_ARCH) endif() message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}") -set(ALL_SANITIZERS asan;radsan;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}") @@ -751,10 +751,10 @@ else() set(COMPILER_RT_HAS_ASAN FALSE) endif() -if (COMPILER_RT_HAS_SANITIZER_COMMON AND RADSAN_SUPPORTED_ARCH) - set(COMPILER_RT_HAS_RADSAN TRUE) +if (COMPILER_RT_HAS_SANITIZER_COMMON AND RTSAN_SUPPORTED_ARCH) + set(COMPILER_RT_HAS_RTSAN TRUE) else() - set(COMPILER_RT_HAS_RADSAN FALSE) + set(COMPILER_RT_HAS_RTSAN FALSE) endif() if (OS_NAME MATCHES "Linux|FreeBSD|Windows|NetBSD|SunOS") diff --git a/compiler-rt/lib/radsan/CMakeLists.txt b/compiler-rt/lib/radsan/CMakeLists.txt deleted file mode 100644 index c7d1dacf3dbc7..0000000000000 --- a/compiler-rt/lib/radsan/CMakeLists.txt +++ /dev/null @@ -1,92 +0,0 @@ -include_directories(..) - -set(RADSAN_CXX_SOURCES - radsan.cpp - radsan_context.cpp - radsan_stack.cpp - radsan_interceptors.cpp) - -set(RADSAN_PREINIT_SOURCES - radsan_preinit.cpp) - -set(RADSAN_HEADERS - radsan.h - radsan_context.h - radsan_stack.h) - -set(RADSAN_DEPS) - -set(RADSAN_CFLAGS - ${COMPILER_RT_COMMON_CFLAGS} - ${COMPILER_RT_CXX_CFLAGS} - -DSANITIZER_COMMON_NO_REDEFINE_BUILTINS) -set(RADSAN_LINK_FLAGS ${COMPILER_RT_COMMON_LINK_FLAGS}) -set(RADSAN_LINK_LIBS - ${COMPILER_RT_UNWINDER_LINK_LIBS} - ${COMPILER_RT_CXX_LINK_LIBS}) - -if(APPLE) - add_compiler_rt_object_libraries(RTRadsan - OS ${SANITIZER_COMMON_SUPPORTED_OS} - ARCHS ${RADSAN_SUPPORTED_ARCH} - SOURCES ${RADSAN_CXX_SOURCES} - ADDITIONAL_HEADERS ${RADSAN_HEADERS} - CFLAGS ${RADSAN_CFLAGS} - DEPS ${RADSAN_DEPS}) -else() - add_compiler_rt_object_libraries(RTRadsan - ARCHS ${RADSAN_SUPPORTED_ARCH} - SOURCES ${RADSAN_CXX_SOURCES} - ADDITIONAL_HEADERS ${RADSAN_HEADERS} - CFLAGS ${RADSAN_CFLAGS} - DEPS ${RADSAN_DEPS}) - add_compiler_rt_object_libraries(RTRadsan_preinit - ARCHS ${RADSAN_SUPPORTED_ARCH} - SOURCES ${RADSAN_PREINIT_SOURCES} - ADDITIONAL_HEADERS ${RADSAN_HEADERS} - CFLAGS ${RADSAN_CFLAGS}) -endif() - -set(RADSAN_COMMON_RUNTIME_OBJECT_LIBS - RTInterception - RTSanitizerCommon - RTSanitizerCommonLibc - RTSanitizerCommonCoverage - RTSanitizerCommonSymbolizer) - -append_list_if(COMPILER_RT_HAS_LIBDL dl RADSAN_LINK_LIBS) -append_list_if(COMPILER_RT_HAS_LIBRT rt RADSAN_LINK_LIBS) -append_list_if(COMPILER_RT_HAS_LIBM m RADSAN_LINK_LIBS) -append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread RADSAN_LINK_LIBS) -append_list_if(COMPILER_RT_HAS_LIBLOG log RADSAN_LINK_LIBS) - -add_compiler_rt_component(radsan) - -if (APPLE) - add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS) - set(RADSAN_LINK_FLAGS ${RADSAN_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}) - - add_compiler_rt_runtime(clang_rt.radsan - SHARED - OS ${SANITIZER_COMMON_SUPPORTED_OS} - ARCHS ${RADSAN_SUPPORTED_ARCH} - OBJECT_LIBS RTRadsan - ${RADSAN_COMMON_RUNTIME_OBJECT_LIBS} - LINK_FLAGS ${RADSAN_LINK_FLAGS} - LINK_LIBS ${RADSAN_LINK_LIBS} - PARENT_TARGET radsan) -else() - add_compiler_rt_runtime(clang_rt.radsan - STATIC - ARCHS ${RADSAN_SUPPORTED_ARCH} - OBJECT_LIBS RTRadsan_preinit - RTRadsan - ${RADSAN_COMMON_RUNTIME_OBJECT_LIBS} - LINK_FLAGS ${RADSAN_LINK_FLAGS} - CFLAGS ${RADSAN_CFLAGS} - PARENT_TARGET radsan) -endif() - -if(COMPILER_RT_INCLUDE_TESTS) - add_subdirectory(tests) -endif() diff --git a/compiler-rt/lib/radsan/radsan.cpp b/compiler-rt/lib/radsan/radsan.cpp deleted file mode 100644 index bd3295e547aa7..0000000000000 --- a/compiler-rt/lib/radsan/radsan.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include -#include -#include -#include - -extern "C" { -RADSAN_EXPORT void radsan_init() { radsan::initialiseInterceptors(); } - -RADSAN_EXPORT void radsan_realtime_enter() { - radsan::getContextForThisThread().realtimePush(); -} - -RADSAN_EXPORT void radsan_realtime_exit() { - radsan::getContextForThisThread().realtimePop(); -} - -RADSAN_EXPORT void radsan_off() { - radsan::getContextForThisThread().bypassPush(); -} - -RADSAN_EXPORT void radsan_on() { - radsan::getContextForThisThread().bypassPop(); -} -} diff --git a/compiler-rt/lib/radsan/radsan.h b/compiler-rt/lib/radsan/radsan.h index 853cddc6927ec..e69de29bb2d1d 100644 --- a/compiler-rt/lib/radsan/radsan.h +++ b/compiler-rt/lib/radsan/radsan.h @@ -1,73 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#pragma once - -#define RADSAN_EXPORT __attribute__((visibility("default"))) - -extern "C" { - -/** - Initialise radsan interceptors. A call to this method is added to the - preinit array on Linux systems. - - @warning Do not call this method as a user. -*/ -RADSAN_EXPORT void radsan_init(); - -/** Enter real-time context. - - When in a real-time context, RADSan interceptors will error if realtime - violations are detected. Calls to this method are injected at the code - generation stage when RADSan is enabled. - - @warning Do not call this method as a user -*/ -RADSAN_EXPORT void radsan_realtime_enter(); - -/** Exit the real-time context. - - When not in a real-time context, RADSan interceptors will simply forward - intercepted method calls to the real methods. - - @warning Do not call this method as a user -*/ -RADSAN_EXPORT void radsan_realtime_exit(); - -/** Disable all RADSan error reporting. - - This method might be useful to you if RADSan is presenting you with an error - for some code you are confident is realtime safe. For example, you might - know that a mutex is never contested, and that locking it will never block - on your particular system. Be careful! - - A call to `radsan_off()` MUST be paired with a corresponding `radsan_on()` - to reactivate interception after the code in question. If you don't, radsan - will cease to work. - - Example: - - [[clang::realtime]] float process (float x) - { - auto const y = 2.0f * x; - - radsan_off(); - i_know_this_method_is_realtime_safe_but_radsan_complains_about_it(); - radsan_on(); - } - -*/ -RADSAN_EXPORT void radsan_off(); - -/** Re-enable all RADSan error reporting. - - The counterpart to `radsan_off`. See the description for `radsan_off` for - details about how to use this method. -*/ -RADSAN_EXPORT void radsan_on(); -} diff --git a/compiler-rt/lib/radsan/radsan_context.cpp b/compiler-rt/lib/radsan/radsan_context.cpp deleted file mode 100644 index 4ec216e219c7d..0000000000000 --- a/compiler-rt/lib/radsan/radsan_context.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include - -#include - -#include -#include - -#include -#include -#include -#include - -using namespace __sanitizer; - -namespace detail { - -static pthread_key_t key; -static pthread_once_t key_once = PTHREAD_ONCE_INIT; -void internalFree(void *ptr) { InternalFree(ptr); } - -} // namespace detail - -namespace radsan { - -Context::Context() = default; - -void Context::realtimePush() { realtime_depth_++; } - -void Context::realtimePop() { realtime_depth_--; } - -void Context::bypassPush() { bypass_depth_++; } - -void Context::bypassPop() { bypass_depth_--; } - -void Context::expectNotRealtime(const char *intercepted_function_name) { - if (inRealtimeContext() && !isBypassed()) { - bypassPush(); - printDiagnostics(intercepted_function_name); - exit(EXIT_FAILURE); - bypassPop(); - } -} - -bool Context::inRealtimeContext() const { return realtime_depth_ > 0; } - -bool Context::isBypassed() const { return bypass_depth_ > 0; } - -void 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); - radsan::printStackTrace(); -} - -Context &getContextForThisThread() { - auto make_tls_key = []() { - CHECK_EQ(pthread_key_create(&detail::key, detail::internalFree), 0); - }; - - pthread_once(&detail::key_once, make_tls_key); - auto *ptr = static_cast(pthread_getspecific(detail::key)); - if (ptr == nullptr) { - ptr = static_cast(InternalAlloc(sizeof(Context))); - new(ptr) Context(); - pthread_setspecific(detail::key, ptr); - } - - return *ptr; -} - -} // namespace radsan diff --git a/compiler-rt/lib/radsan/radsan_context.h b/compiler-rt/lib/radsan/radsan_context.h deleted file mode 100644 index 66d810ce97735..0000000000000 --- a/compiler-rt/lib/radsan/radsan_context.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#pragma once - -namespace radsan { - -class Context { -public: - Context(); - - void realtimePush(); - void realtimePop(); - - void bypassPush(); - void bypassPop(); - - void expectNotRealtime(const char *interpreted_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 radsan diff --git a/compiler-rt/lib/radsan/radsan_interceptors.h b/compiler-rt/lib/radsan/radsan_interceptors.h deleted file mode 100644 index 7a7471d3ad23b..0000000000000 --- a/compiler-rt/lib/radsan/radsan_interceptors.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#pragma once - -namespace radsan { -void initialiseInterceptors(); -} diff --git a/compiler-rt/lib/radsan/radsan_preinit.cpp b/compiler-rt/lib/radsan/radsan_preinit.cpp deleted file mode 100644 index f130dadb92e4b..0000000000000 --- a/compiler-rt/lib/radsan/radsan_preinit.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include "sanitizer_common/sanitizer_internal_defs.h" -#include - -#if SANITIZER_CAN_USE_PREINIT_ARRAY - -// The symbol is called __local_radsan_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_radsan_preinit)(void) = radsan_init; - -#endif diff --git a/compiler-rt/lib/radsan/radsan_stack.h b/compiler-rt/lib/radsan/radsan_stack.h deleted file mode 100644 index 2f702079ff969..0000000000000 --- a/compiler-rt/lib/radsan/radsan_stack.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#pragma once - -namespace radsan { -void printStackTrace(); -} diff --git a/compiler-rt/lib/radsan/tests/CMakeLists.txt b/compiler-rt/lib/radsan/tests/CMakeLists.txt deleted file mode 100644 index 73cedac2765d6..0000000000000 --- a/compiler-rt/lib/radsan/tests/CMakeLists.txt +++ /dev/null @@ -1,103 +0,0 @@ -include(CompilerRTCompile) - -include_directories(..) - -set(RADSAN_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/radsan - -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/tests - -DSANITIZER_COMMON_NO_REDEFINE_BUILTINS - -O2) - -set(RADSAN_INST_TEST_SOURCES - radsan_test.cpp - radsan_test_interceptors.cpp - radsan_test_main.cpp) - -set(RADSAN_NOINST_TEST_SOURCES - ../radsan_preinit.cpp - radsan_test_context.cpp - radsan_test_main.cpp) - -set(RADSAN_UNITTEST_HEADERS - radsan_test_utilities.h) - -add_custom_target(RadsanUnitTests) -set_target_properties(RadsanUnitTests PROPERTIES FOLDER "Compiler-RT Tests") - -set(RADSAN_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 RADSAN_UNITTEST_LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS}) - list(APPEND RADSAN_UNITTEST_LINK_FLAGS ${DARWIN_osx_LINK_FLAGS}) - list(APPEND RADSAN_UNITTEST_CFLAGS ${DARWIN_osx_CFLAGS}) -else() - list(APPEND RADSAN_UNITTEST_LINK_FLAGS -latomic) -endif() - -set(COMPILER_RT_GOOGLETEST_SOURCES ${COMPILER_RT_GTEST_SOURCE} ${COMPILER_RT_GMOCK_SOURCE}) - -set(RADSAN_TEST_ARCH ${RADSAN_SUPPORTED_ARCH}) -if(APPLE) - darwin_filter_host_archs(RADSAN_SUPPORTED_ARCH RADSAN_TEST_ARCH) -endif() - -foreach(arch ${RADSAN_TEST_ARCH}) - set(RadsanTestObjects) - generate_compiler_rt_tests(RadsanTestObjects - RadsanUnitTests "Radsan-${arch}-Test" ${arch} - COMPILE_DEPS ${RADSAN_UNITTEST_HEADERS} - SOURCES ${RADSAN_INST_TEST_SOURCES} ${COMPILER_RT_GOOGLETEST_SOURCES} - DEPS llvm_gtest radsan - CFLAGS ${RADSAN_UNITTEST_CFLAGS} -fsanitize=realtime - LINK_FLAGS ${RADSAN_UNITTEST_LINK_FLAGS} -fsanitize=realtime) - set_target_properties(RadsanUnitTests PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - set(RADSAN_TEST_RUNTIME RTRadsanTest.${arch}) - if(APPLE) - set(RADSAN_TEST_RUNTIME_OBJECTS - $ - $ - $ - $ - $ - $) - else() - set(RADSAN_TEST_RUNTIME_OBJECTS - $ - $ - $ - $ - $ - $ - $) - endif() - add_library(${RADSAN_TEST_RUNTIME} STATIC ${RADSAN_TEST_RUNTIME_OBJECTS}) - set_target_properties(${RADSAN_TEST_RUNTIME} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - FOLDER "Compiler-RT Runtime tests") - - set(RadsanNoInstTestObjects) - generate_compiler_rt_tests(RadsanNoInstTestObjects - RadsanUnitTests "Radsan-${arch}-NoInstTest" ${arch} - COMPILE_DEPS ${RADSAN_UNITTEST_HEADERS} - SOURCES ${RADSAN_NOINST_TEST_SOURCES} - ${COMPILER_RT_GOOGLETEST_SOURCES} - DEPS llvm_gtest - CFLAGS ${RADSAN_UNITTEST_CFLAGS} - LINK_FLAGS ${RADSAN_UNITTEST_LINK_FLAGS} - RUNTIME ${RADSAN_TEST_RUNTIME}) - set_target_properties(RadsanUnitTests PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) -endforeach() diff --git a/compiler-rt/lib/radsan/tests/radsan_test.cpp b/compiler-rt/lib/radsan/tests/radsan_test.cpp deleted file mode 100644 index 9e95110adaad9..0000000000000 --- a/compiler-rt/lib/radsan/tests/radsan_test.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include "gtest/gtest.h" - -#include "radsan_test_utilities.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 RADSAN_TEST_SHARED_MUTEX (!(SI_MAC) || SI_MAC_DEPLOYMENT_AT_LEAST_10_12) - -using namespace testing; -using namespace radsan_testing; -using namespace std::chrono_literals; - -namespace { -void invokeStdFunction(std::function &&function) { function(); } -} // namespace - -TEST(TestRadsan, vectorPushBackAllocationDiesWhenRealtime) { - auto vec = std::vector{}; - auto func = [&vec]() { vec.push_back(0.4f); }; - expectRealtimeDeath(func); - ASSERT_EQ(0u, vec.size()); - expectNonrealtimeSurvival(func); - ASSERT_EQ(1u, vec.size()); -} - -TEST(TestRadsan, destructionOfObjectOnHeapDiesWhenRealtime) { - auto obj = std::make_unique>(); - auto func = [&obj]() { obj.reset(); }; - expectRealtimeDeath(func); - ASSERT_NE(nullptr, obj.get()); - expectNonrealtimeSurvival(func); - ASSERT_EQ(nullptr, obj.get()); -} - -TEST(TestRadsan, sleepingAThreadDiesWhenRealtime) { - auto func = []() { std::this_thread::sleep_for(1us); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, ifstreamCreationDiesWhenRealtime) { - auto func = []() { auto ifs = std::ifstream("./file.txt"); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); - std::remove("./file.txt"); -} - -TEST(TestRadsan, ofstreamCreationDiesWhenRealtime) { - auto func = []() { auto ofs = std::ofstream("./file.txt"); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); - std::remove("./file.txt"); -} - -TEST(TestRadsan, lockingAMutexDiesWhenRealtime) { - auto mutex = std::mutex{}; - auto func = [&]() { mutex.lock(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, unlockingAMutexDiesWhenRealtime) { - auto mutex = std::mutex{}; - mutex.lock(); - auto func = [&]() { mutex.unlock(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -#if RADSAN_TEST_SHARED_MUTEX - -TEST(TestRadsan, lockingASharedMutexDiesWhenRealtime) { - auto mutex = std::shared_mutex(); - auto func = [&]() { mutex.lock(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, unlockingASharedMutexDiesWhenRealtime) { - auto mutex = std::shared_mutex(); - mutex.lock(); - auto func = [&]() { mutex.unlock(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, sharedLockingASharedMutexDiesWhenRealtime) { - auto mutex = std::shared_mutex(); - auto func = [&]() { mutex.lock_shared(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, sharedUnlockingASharedMutexDiesWhenRealtime) { - auto mutex = std::shared_mutex(); - mutex.lock_shared(); - auto func = [&]() { mutex.unlock_shared(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -#endif // RADSAN_TEST_SHARED_MUTEX - -TEST(TestRadsan, launchingAThreadDiesWhenRealtime) { - auto func = [&]() { - auto t = std::thread([]() {}); - t.join(); - }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, copyingALambdaWithLargeCaptureDiesWhenRealtime) { - auto lots_of_data = std::array{}; - 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(TestRadsan, accessingALargeAtomicVariableDiesWhenRealtime) { - auto small_atomic = std::atomic{0.0f}; - ASSERT_TRUE(small_atomic.is_lock_free()); - realtimeInvoke([&small_atomic]() { auto x = small_atomic.load(); }); - - auto large_atomic = std::atomic>{{}}; - ASSERT_FALSE(large_atomic.is_lock_free()); - auto func = [&]() { auto x = large_atomic.load(); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, firstCoutDiesWhenRealtime) { - auto func = []() { std::cout << "Hello, world!" << std::endl; }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, secondCoutDiesWhenRealtime) { - std::cout << "Hello, world"; - auto func = []() { std::cout << "Hello, again!" << std::endl; }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, printfDiesWhenRealtime) { - auto func = []() { printf("Hello, world!\n"); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, throwingAnExceptionDiesWhenRealtime) { - auto func = [&]() { - try { - throw std::exception(); - } catch (std::exception &) { - } - }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsan, doesNotDieIfTurnedOff) { - auto mutex = std::mutex{}; - auto realtime_unsafe_func = [&]() { - radsan_off(); - mutex.lock(); - mutex.unlock(); - radsan_on(); - }; - realtimeInvoke(realtime_unsafe_func); -} diff --git a/compiler-rt/lib/radsan/tests/radsan_test_context.cpp b/compiler-rt/lib/radsan/tests/radsan_test_context.cpp deleted file mode 100644 index 5bf48362f819a..0000000000000 --- a/compiler-rt/lib/radsan/tests/radsan_test_context.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include "radsan_test_utilities.h" - -#include "radsan_context.h" - -TEST(TestRadsanContext, canCreateContext) { auto context = radsan::Context{}; } - -TEST(TestRadsanContext, expectNotRealtimeDoesNotDieBeforeRealtimePush) { - auto context = radsan::Context{}; - context.expectNotRealtime("do_some_stuff"); -} - -TEST(TestRadsanContext, expectNotRealtimeDoesNotDieAfterPushAndPop) { - auto context = radsan::Context{}; - context.realtimePush(); - context.realtimePop(); - context.expectNotRealtime("do_some_stuff"); -} - -TEST(TestRadsanContext, expectNotRealtimeDiesAfterRealtimePush) { - auto context = radsan::Context{}; - - context.realtimePush(); - EXPECT_DEATH(context.expectNotRealtime("do_some_stuff"), ""); -} - -TEST(TestRadsanContext, - expectNotRealtimeDiesAfterRealtimeAfterMorePushesThanPops) { - auto context = radsan::Context{}; - - context.realtimePush(); - context.realtimePush(); - context.realtimePush(); - context.realtimePop(); - context.realtimePop(); - EXPECT_DEATH(context.expectNotRealtime("do_some_stuff"), ""); -} - -TEST(TestRadsanContext, expectNotRealtimeDoesNotDieAfterBypassPush) { - auto context = radsan::Context{}; - - context.realtimePush(); - context.bypassPush(); - context.expectNotRealtime("do_some_stuff"); -} - -TEST(TestRadsanContext, - expectNotRealtimeDoesNotDieIfBypassDepthIsGreaterThanZero) { - auto context = radsan::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/radsan/tests/radsan_test_interceptors.cpp b/compiler-rt/lib/radsan/tests/radsan_test_interceptors.cpp deleted file mode 100644 index 316c294ba8294..0000000000000 --- a/compiler-rt/lib/radsan/tests/radsan_test_interceptors.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include "gtest/gtest.h" - -#include -#include - -#include "radsan_test_utilities.h" - -#if SANITIZER_APPLE -#include -#include -#endif - -#if SANITIZER_INTERCEPT_MEMALIGN || SANITIZER_INTERCEPT_PVALLOC -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -using namespace testing; -using namespace radsan_testing; -using namespace std::chrono_literals; - -namespace { -void *fake_thread_entry_point(void *) { return nullptr; } -} // namespace - -class RadsanFileTest : public ::testing::Test { -protected: - void SetUp() override { - const ::testing::TestInfo *const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - file_path_ = std::string("/tmp/radsan_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(TestRadsanInterceptors, mallocDiesWhenRealtime) { - auto func = []() { EXPECT_NE(nullptr, malloc(1)); }; - expectRealtimeDeath(func, "malloc"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, reallocDiesWhenRealtime) { - auto *ptr_1 = malloc(1); - auto func = [ptr_1]() { EXPECT_NE(nullptr, realloc(ptr_1, 8)); }; - expectRealtimeDeath(func, "realloc"); - expectNonrealtimeSurvival(func); -} - -#if SANITIZER_APPLE -TEST(TestRadsanInterceptors, reallocfDiesWhenRealtime) { - auto *ptr_1 = malloc(1); - auto func = [ptr_1]() { EXPECT_NE(nullptr, reallocf(ptr_1, 8)); }; - expectRealtimeDeath(func, "reallocf"); - expectNonrealtimeSurvival(func); -} -#endif - -TEST(TestRadsanInterceptors, vallocDiesWhenRealtime) { - auto func = []() { EXPECT_NE(nullptr, valloc(4)); }; - expectRealtimeDeath(func, "valloc"); - expectNonrealtimeSurvival(func); -} - -#if SANITIZER_INTERCEPT_ALIGNED_ALLOC -TEST(TestRadsanInterceptors, 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(TestRadsanInterceptors, freeDiesWhenRealtime) { - auto *ptr_1 = malloc(1); - auto *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(TestRadsanInterceptors, freeSurvivesWhenRealtimeIfArgumentIsNull) { - realtimeInvoke([]() { free(NULL); }); - expectNonrealtimeSurvival([]() { free(NULL); }); -} - -TEST(TestRadsanInterceptors, posixMemalignDiesWhenRealtime) { - auto func = []() { - void *mem; - posix_memalign(&mem, 4, 4); - }; - expectRealtimeDeath(func, "posix_memalign"); - expectNonrealtimeSurvival(func); -} - -#if SANITIZER_INTERCEPT_MEMALIGN -TEST(TestRadsanInterceptors, memalignDiesWhenRealtime) { - auto func = []() { EXPECT_NE(memalign(2, 2048), nullptr); }; - expectRealtimeDeath(func, "memalign"); - expectNonrealtimeSurvival(func); -} -#endif - -#if SANITIZER_INTERCEPT_PVALLOC -TEST(TestRadsanInterceptors, pvallocDiesWhenRealtime) { - auto func = []() { EXPECT_NE(pvalloc(2048), nullptr); }; - expectRealtimeDeath(func, "pvalloc"); - expectNonrealtimeSurvival(func); -} -#endif - -/* - Sleeping -*/ - -TEST(TestRadsanInterceptors, sleepDiesWhenRealtime) { - auto func = []() { sleep(0u); }; - expectRealtimeDeath(func, "sleep"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, usleepDiesWhenRealtime) { - auto func = []() { usleep(1u); }; - expectRealtimeDeath(func, "usleep"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, nanosleepDiesWhenRealtime) { - auto func = []() { - auto t = timespec{}; - nanosleep(&t, &t); - }; - expectRealtimeDeath(func, "nanosleep"); - expectNonrealtimeSurvival(func); -} - -/* - Filesystem -*/ - -TEST_F(RadsanFileTest, openDiesWhenRealtime) { - auto func = [this]() { open(GetTemporaryFilePath(), O_RDONLY); }; - expectRealtimeDeath(func, "open"); - expectNonrealtimeSurvival(func); -} - -TEST_F(RadsanFileTest, openatDiesWhenRealtime) { - auto func = [this]() { openat(0, GetTemporaryFilePath(), O_RDONLY); }; - expectRealtimeDeath(func, "openat"); - expectNonrealtimeSurvival(func); -} - -TEST_F(RadsanFileTest, 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(RadsanFileTest, creatDiesWhenRealtime) { - auto func = [this]() { creat(GetTemporaryFilePath(), S_IWOTH | S_IROTH); }; - expectRealtimeDeath(func, "creat"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, fcntlDiesWhenRealtime) { - auto func = []() { fcntl(0, F_GETFL); }; - expectRealtimeDeath(func, "fcntl"); - expectNonrealtimeSurvival(func); -} - -TEST_F(RadsanFileTest, 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(RadsanFileTest, 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(TestRadsanInterceptors, closeDiesWhenRealtime) { - auto func = []() { close(0); }; - expectRealtimeDeath(func, "close"); - expectNonrealtimeSurvival(func); -} - -TEST_F(RadsanFileTest, fopenDiesWhenRealtime) { - auto func = [this]() { - auto fd = fopen(GetTemporaryFilePath(), "w"); - EXPECT_THAT(fd, Ne(nullptr)); - }; - expectRealtimeDeath(func, "fopen"); - expectNonrealtimeSurvival(func); -} - -TEST_F(RadsanFileTest, 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(RadsanFileTest, 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(RadsanFileTest, fcloseDiesWhenRealtime) { - auto fd = fopen(GetTemporaryFilePath(), "w"); - EXPECT_THAT(fd, Ne(nullptr)); - auto func = [fd]() { fclose(fd); }; - expectRealtimeDeath(func, "fclose"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, putsDiesWhenRealtime) { - auto func = []() { puts("Hello, world!\n"); }; - expectRealtimeDeath(func); - expectNonrealtimeSurvival(func); -} - -TEST_F(RadsanFileTest, 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(TestRadsanInterceptors, pthreadCreateDiesWhenRealtime) { - auto func = []() { - auto thread = pthread_t{}; - auto const attr = pthread_attr_t{}; - struct thread_info *tinfo; - pthread_create(&thread, &attr, &fake_thread_entry_point, tinfo); - }; - expectRealtimeDeath(func, "pthread_create"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadMutexLockDiesWhenRealtime) { - auto func = []() { - auto mutex = pthread_mutex_t{}; - pthread_mutex_lock(&mutex); - }; - - expectRealtimeDeath(func, "pthread_mutex_lock"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadMutexUnlockDiesWhenRealtime) { - auto func = []() { - auto mutex = pthread_mutex_t{}; - pthread_mutex_unlock(&mutex); - }; - - expectRealtimeDeath(func, "pthread_mutex_unlock"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadMutexJoinDiesWhenRealtime) { - auto func = []() { - auto thread = pthread_t{}; - 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(TestRadsanInterceptors, osSpinLockLockDiesWhenRealtime) { - auto func = []() { - auto spin_lock = OSSpinLock{}; - OSSpinLockLock(&spin_lock); - }; - expectRealtimeDeath(func, "OSSpinLockLock"); - expectNonrealtimeSurvival(func); -} -#pragma clang diagnostic pop - -TEST(TestRadsanInterceptors, osUnfairLockLockDiesWhenRealtime) { - auto func = []() { - auto unfair_lock = os_unfair_lock_s{}; - os_unfair_lock_lock(&unfair_lock); - }; - expectRealtimeDeath(func, "os_unfair_lock_lock"); - expectNonrealtimeSurvival(func); -} -#endif - -#if SANITIZER_LINUX -TEST(TestRadsanInterceptors, spinLockLockDiesWhenRealtime) { - auto spinlock = pthread_spinlock_t{}; - pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED); - auto func = [&]() { pthread_spin_lock(&spinlock); }; - expectRealtimeDeath(func, "pthread_spin_lock"); - expectNonrealtimeSurvival(func); -} -#endif - -TEST(TestRadsanInterceptors, pthreadCondSignalDiesWhenRealtime) { - auto func = []() { - auto cond = pthread_cond_t{}; - pthread_cond_signal(&cond); - }; - expectRealtimeDeath(func, "pthread_cond_signal"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadCondBroadcastDiesWhenRealtime) { - auto func = []() { - auto cond = pthread_cond_t{}; - pthread_cond_broadcast(&cond); - }; - expectRealtimeDeath(func, "pthread_cond_broadcast"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadCondWaitDiesWhenRealtime) { - auto cond = pthread_cond_t{}; - auto mutex = pthread_mutex_t{}; - 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. -} - -TEST(TestRadsanInterceptors, pthreadRwlockRdlockDiesWhenRealtime) { - auto func = []() { - auto rwlock = pthread_rwlock_t{}; - pthread_rwlock_rdlock(&rwlock); - }; - expectRealtimeDeath(func, "pthread_rwlock_rdlock"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadRwlockUnlockDiesWhenRealtime) { - auto func = []() { - auto rwlock = pthread_rwlock_t{}; - pthread_rwlock_unlock(&rwlock); - }; - expectRealtimeDeath(func, "pthread_rwlock_unlock"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, pthreadRwlockWrlockDiesWhenRealtime) { - auto func = []() { - auto rwlock = pthread_rwlock_t{}; - pthread_rwlock_wrlock(&rwlock); - }; - expectRealtimeDeath(func, "pthread_rwlock_wrlock"); - expectNonrealtimeSurvival(func); -} - -/* - Sockets -*/ -TEST(TestRadsanInterceptors, openingASocketDiesWhenRealtime) { - auto func = []() { socket(PF_INET, SOCK_STREAM, 0); }; - expectRealtimeDeath(func, "socket"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, sendToASocketDiesWhenRealtime) { - auto func = []() { send(0, nullptr, 0, 0); }; - expectRealtimeDeath(func, "send"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, sendmsgToASocketDiesWhenRealtime) { - auto const msg = msghdr{}; - auto func = [&]() { sendmsg(0, &msg, 0); }; - expectRealtimeDeath(func, "sendmsg"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, sendtoToASocketDiesWhenRealtime) { - auto const addr = sockaddr{}; - auto const len = socklen_t{}; - auto func = [&]() { sendto(0, nullptr, 0, 0, &addr, len); }; - expectRealtimeDeath(func, "sendto"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, recvFromASocketDiesWhenRealtime) { - auto func = []() { recv(0, nullptr, 0, 0); }; - expectRealtimeDeath(func, "recv"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, recvfromOnASocketDiesWhenRealtime) { - auto addr = sockaddr{}; - auto len = socklen_t{}; - auto func = [&]() { recvfrom(0, nullptr, 0, 0, &addr, &len); }; - expectRealtimeDeath(func, "recvfrom"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, recvmsgOnASocketDiesWhenRealtime) { - auto msg = msghdr{}; - auto func = [&]() { recvmsg(0, &msg, 0); }; - expectRealtimeDeath(func, "recvmsg"); - expectNonrealtimeSurvival(func); -} - -TEST(TestRadsanInterceptors, shutdownOnASocketDiesWhenRealtime) { - auto func = [&]() { shutdown(0, 0); }; - expectRealtimeDeath(func, "shutdown"); - expectNonrealtimeSurvival(func); -} diff --git a/compiler-rt/lib/radsan/tests/radsan_test_main.cpp b/compiler-rt/lib/radsan/tests/radsan_test_main.cpp deleted file mode 100644 index f75dc2be2f0a1..0000000000000 --- a/compiler-rt/lib/radsan/tests/radsan_test_main.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#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/radsan/tests/radsan_test_utilities.h b/compiler-rt/lib/radsan/tests/radsan_test_utilities.h deleted file mode 100644 index fb50ee77156e9..0000000000000 --- a/compiler-rt/lib/radsan/tests/radsan_test_utilities.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#pragma once - -#include "gmock/gmock.h" -#include - -namespace radsan_testing { - -template -[[clang::realtime]] void realtimeInvoke(Function &&func) { - std::forward(func)(); -} - -template -void expectRealtimeDeath(Function &&func, - const char *intercepted_method_name = nullptr) { - - using namespace testing; - - auto expected_error_substr = [&]() -> 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(func)), - ExitedWithCode(EXIT_FAILURE), expected_error_substr()); -} - -template void expectNonrealtimeSurvival(Function &&func) { - std::forward(func)(); -} - -} // namespace radsan_testing diff --git a/compiler-rt/lib/rtsan/CMakeLists.txt b/compiler-rt/lib/rtsan/CMakeLists.txt new file mode 100644 index 0000000000000..bd7358e86e59b --- /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 0000000000000..43a63b4636f1e --- /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 +#include +#include + +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 0000000000000..8b1219c13cbd3 --- /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 0000000000000..f761ddedce672 --- /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 + +#include + +#include +#include + +#include +#include +#include +#include + +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 0000000000000..515bb8ade1eb9 --- /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/radsan/radsan_interceptors.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors.cpp similarity index 75% rename from compiler-rt/lib/radsan/radsan_interceptors.cpp rename to compiler-rt/lib/rtsan/rtsan_interceptors.cpp index 2ff174d5118fc..3a65f9d3f779d 100644 --- a/compiler-rt/lib/radsan/radsan_interceptors.cpp +++ b/compiler-rt/lib/rtsan/rtsan_interceptors.cpp @@ -1,22 +1,20 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ - -#include "radsan/radsan_interceptors.h" +//===--- 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 "radsan/radsan_context.h" - -#if !SANITIZER_LINUX && !SANITIZER_APPLE -#error Sorry, radsan does not yet support this platform -#endif +#include "rtsan/rtsan_context.h" #if SANITIZER_APPLE #include @@ -37,20 +35,17 @@ using namespace __sanitizer; -namespace radsan { -void expectNotRealtime(const char *intercepted_function_name) { - getContextForThisThread().expectNotRealtime(intercepted_function_name); +void ExpectNotRealtime(const char *intercepted_function_name) { + __rtsan::GetContextForThisThread().ExpectNotRealtime( + intercepted_function_name); } -} // namespace radsan -/* - Filesystem -*/ +// Filesystem INTERCEPTOR(int, open, const char *path, int oflag, ...) { // TODO Establish whether we should intercept here if the flag contains // O_NONBLOCK - radsan::expectNotRealtime("open"); + ExpectNotRealtime("open"); va_list args; va_start(args, oflag); @@ -64,7 +59,7 @@ INTERCEPTOR(int, open, const char *path, int oflag, ...) { INTERCEPTOR(int, openat, int fd, const char *path, int oflag, ...) { // TODO Establish whether we should intercept here if the flag contains // O_NONBLOCK - radsan::expectNotRealtime("openat"); + ExpectNotRealtime("openat"); va_list args; va_start(args, oflag); @@ -78,13 +73,13 @@ INTERCEPTOR(int, openat, int fd, const char *path, int oflag, ...) { INTERCEPTOR(int, creat, const char *path, mode_t mode) { // TODO Establish whether we should intercept here if the flag contains // O_NONBLOCK - radsan::expectNotRealtime("creat"); - auto result = REAL(creat)(path, mode); + ExpectNotRealtime("creat"); + const int result = REAL(creat)(path, mode); return result; } INTERCEPTOR(int, fcntl, int filedes, int cmd, ...) { - radsan::expectNotRealtime("fcntl"); + ExpectNotRealtime("fcntl"); va_list args; va_start(args, cmd); @@ -105,274 +100,257 @@ INTERCEPTOR(int, fcntl, int filedes, int cmd, ...) { } INTERCEPTOR(int, close, int filedes) { - radsan::expectNotRealtime("close"); + ExpectNotRealtime("close"); return REAL(close)(filedes); } INTERCEPTOR(FILE *, fopen, const char *path, const char *mode) { - radsan::expectNotRealtime("fopen"); + ExpectNotRealtime("fopen"); return REAL(fopen)(path, mode); } INTERCEPTOR(size_t, fread, void *ptr, size_t size, size_t nitems, FILE *stream) { - radsan::expectNotRealtime("fread"); + ExpectNotRealtime("fread"); return REAL(fread)(ptr, size, nitems, stream); } INTERCEPTOR(size_t, fwrite, const void *ptr, size_t size, size_t nitems, FILE *stream) { - radsan::expectNotRealtime("fwrite"); + ExpectNotRealtime("fwrite"); return REAL(fwrite)(ptr, size, nitems, stream); } INTERCEPTOR(int, fclose, FILE *stream) { - radsan::expectNotRealtime("fclose"); + ExpectNotRealtime("fclose"); return REAL(fclose)(stream); } INTERCEPTOR(int, fputs, const char *s, FILE *stream) { - radsan::expectNotRealtime("fputs"); + ExpectNotRealtime("fputs"); return REAL(fputs)(s, stream); } -/* - Streams -*/ - +// Streams INTERCEPTOR(int, puts, const char *s) { - radsan::expectNotRealtime("puts"); + ExpectNotRealtime("puts"); return REAL(puts)(s); } -/* - Concurrency -*/ - +// 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) { - radsan::expectNotRealtime("OSSpinLockLock"); + ExpectNotRealtime("OSSpinLockLock"); return REAL(OSSpinLockLock)(lock); } #pragma clang diagnostic pop INTERCEPTOR(void, os_unfair_lock_lock, os_unfair_lock_t lock) { - radsan::expectNotRealtime("os_unfair_lock_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) { - radsan::expectNotRealtime("pthread_spin_lock"); + 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) { - radsan::expectNotRealtime("pthread_create"); + ExpectNotRealtime("pthread_create"); return REAL(pthread_create)(thread, attr, start_routine, arg); } INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *mutex) { - radsan::expectNotRealtime("pthread_mutex_lock"); + ExpectNotRealtime("pthread_mutex_lock"); return REAL(pthread_mutex_lock)(mutex); } INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *mutex) { - radsan::expectNotRealtime("pthread_mutex_unlock"); + ExpectNotRealtime("pthread_mutex_unlock"); return REAL(pthread_mutex_unlock)(mutex); } INTERCEPTOR(int, pthread_join, pthread_t thread, void **value_ptr) { - radsan::expectNotRealtime("pthread_join"); + ExpectNotRealtime("pthread_join"); return REAL(pthread_join)(thread, value_ptr); } INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *cond) { - radsan::expectNotRealtime("pthread_cond_signal"); + ExpectNotRealtime("pthread_cond_signal"); return REAL(pthread_cond_signal)(cond); } INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *cond) { - radsan::expectNotRealtime("pthread_cond_broadcast"); + ExpectNotRealtime("pthread_cond_broadcast"); return REAL(pthread_cond_broadcast)(cond); } INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *cond, pthread_mutex_t *mutex) { - radsan::expectNotRealtime("pthread_cond_wait"); + 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) { - radsan::expectNotRealtime("pthread_cond_timedwait"); + ExpectNotRealtime("pthread_cond_timedwait"); return REAL(pthread_cond_timedwait)(cond, mutex, ts); } INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *lock) { - radsan::expectNotRealtime("pthread_rwlock_rdlock"); + ExpectNotRealtime("pthread_rwlock_rdlock"); return REAL(pthread_rwlock_rdlock)(lock); } INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *lock) { - radsan::expectNotRealtime("pthread_rwlock_unlock"); + ExpectNotRealtime("pthread_rwlock_unlock"); return REAL(pthread_rwlock_unlock)(lock); } INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *lock) { - radsan::expectNotRealtime("pthread_rwlock_wrlock"); + ExpectNotRealtime("pthread_rwlock_wrlock"); return REAL(pthread_rwlock_wrlock)(lock); } -/* - Sleeping -*/ +// Sleeping INTERCEPTOR(unsigned int, sleep, unsigned int s) { - radsan::expectNotRealtime("sleep"); + ExpectNotRealtime("sleep"); return REAL(sleep)(s); } INTERCEPTOR(int, usleep, useconds_t u) { - radsan::expectNotRealtime("usleep"); + ExpectNotRealtime("usleep"); return REAL(usleep)(u); } INTERCEPTOR(int, nanosleep, const struct timespec *rqtp, struct timespec *rmtp) { - radsan::expectNotRealtime("nanosleep"); + ExpectNotRealtime("nanosleep"); return REAL(nanosleep)(rqtp, rmtp); } -/* - Memory -*/ +// Memory INTERCEPTOR(void *, calloc, SIZE_T num, SIZE_T size) { - radsan::expectNotRealtime("calloc"); + ExpectNotRealtime("calloc"); return REAL(calloc)(num, size); } INTERCEPTOR(void, free, void *ptr) { if (ptr != NULL) { - radsan::expectNotRealtime("free"); + ExpectNotRealtime("free"); } return REAL(free)(ptr); } INTERCEPTOR(void *, malloc, SIZE_T size) { - radsan::expectNotRealtime("malloc"); + ExpectNotRealtime("malloc"); return REAL(malloc)(size); } INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { - radsan::expectNotRealtime("realloc"); + ExpectNotRealtime("realloc"); return REAL(realloc)(ptr, size); } INTERCEPTOR(void *, reallocf, void *ptr, SIZE_T size) { - radsan::expectNotRealtime("reallocf"); + ExpectNotRealtime("reallocf"); return REAL(reallocf)(ptr, size); } INTERCEPTOR(void *, valloc, SIZE_T size) { - radsan::expectNotRealtime("valloc"); + ExpectNotRealtime("valloc"); return REAL(valloc)(size); } #if SANITIZER_INTERCEPT_ALIGNED_ALLOC INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) { - radsan::expectNotRealtime("aligned_alloc"); + ExpectNotRealtime("aligned_alloc"); return REAL(aligned_alloc)(alignment, size); } -#define RADSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc) +#define RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc) #else -#define RADSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC +#define RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC #endif INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { - radsan::expectNotRealtime("posix_memalign"); + ExpectNotRealtime("posix_memalign"); return REAL(posix_memalign)(memptr, alignment, size); } #if SANITIZER_INTERCEPT_MEMALIGN INTERCEPTOR(void *, memalign, size_t alignment, size_t size) { - radsan::expectNotRealtime("memalign"); + ExpectNotRealtime("memalign"); return REAL(memalign)(alignment, size); } #endif #if SANITIZER_INTERCEPT_PVALLOC INTERCEPTOR(void *, pvalloc, size_t size) { - radsan::expectNotRealtime("pvalloc"); + ExpectNotRealtime("pvalloc"); return REAL(pvalloc)(size); } #endif -/* - Sockets -*/ - +// Sockets INTERCEPTOR(int, socket, int domain, int type, int protocol) { - radsan::expectNotRealtime("socket"); + ExpectNotRealtime("socket"); return REAL(socket)(domain, type, protocol); } INTERCEPTOR(ssize_t, send, int sockfd, const void *buf, size_t len, int flags) { - radsan::expectNotRealtime("send"); + ExpectNotRealtime("send"); return REAL(send)(sockfd, buf, len, flags); } INTERCEPTOR(ssize_t, sendmsg, int socket, const struct msghdr *message, int flags) { - radsan::expectNotRealtime("sendmsg"); + 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) { - radsan::expectNotRealtime("sendto"); + 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) { - radsan::expectNotRealtime("recv"); + 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) { - radsan::expectNotRealtime("recvfrom"); + ExpectNotRealtime("recvfrom"); return REAL(recvfrom)(socket, buffer, length, flags, address, address_len); } INTERCEPTOR(ssize_t, recvmsg, int socket, struct msghdr *message, int flags) { - radsan::expectNotRealtime("recvmsg"); + ExpectNotRealtime("recvmsg"); return REAL(recvmsg)(socket, message, flags); } INTERCEPTOR(int, shutdown, int socket, int how) { - radsan::expectNotRealtime("shutdown"); + ExpectNotRealtime("shutdown"); return REAL(shutdown)(socket, how); } -/* - Preinit -*/ - -namespace radsan { -void initialiseInterceptors() { +// Preinit +void __rtsan::InitializeInterceptors() { INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(free); INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(realloc); INTERCEPT_FUNCTION(reallocf); INTERCEPT_FUNCTION(valloc); - RADSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC; + RTSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC; INTERCEPT_FUNCTION(posix_memalign); #if SANITIZER_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign); @@ -425,4 +403,3 @@ void initialiseInterceptors() { INTERCEPT_FUNCTION(recvfrom); INTERCEPT_FUNCTION(shutdown); } -} // namespace radsan diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors.h b/compiler-rt/lib/rtsan/rtsan_interceptors.h new file mode 100644 index 0000000000000..a3dac27544c66 --- /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 0000000000000..8cea61c3ea8b7 --- /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 + +#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/radsan/radsan_stack.cpp b/compiler-rt/lib/rtsan/rtsan_stack.cpp similarity index 56% rename from compiler-rt/lib/radsan/radsan_stack.cpp rename to compiler-rt/lib/rtsan/rtsan_stack.cpp index 4b9c98c6cb000..43fd5fbf05509 100644 --- a/compiler-rt/lib/radsan/radsan_stack.cpp +++ b/compiler-rt/lib/rtsan/rtsan_stack.cpp @@ -1,19 +1,23 @@ -/** - This file is part of the RealtimeSanitizer (RADSan) project. - https://github.com/realtime-sanitizer/radsan - - Copyright 2023 David Trevelyan & Alistair Barker - Subject to GNU General Public License (GPL) v3.0 -*/ +//===--- 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 #include 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) { @@ -25,26 +29,22 @@ void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, } } // namespace __sanitizer -namespace { -void setGlobalStackTraceFormat() { +static void SetGlobalStackTraceFormat() { SetCommonFlagsDefaults(); CommonFlags cf; cf.CopyFrom(*common_flags()); cf.stack_trace_format = "DEFAULT"; - cf.external_symbolizer_path = GetEnv("RADSAN_SYMBOLIZER_PATH"); + cf.external_symbolizer_path = GetEnv("RTSAN_SYMBOLIZER_PATH"); OverrideCommonFlags(cf); } -} // namespace -namespace radsan { -void printStackTrace() { +void __rtsan::PrintStackTrace() { - auto stack = BufferedStackTrace{}; + BufferedStackTrace stack{}; GET_CURRENT_PC_BP; stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); - setGlobalStackTraceFormat(); + SetGlobalStackTraceFormat(); stack.Print(); } -} // namespace radsan diff --git a/compiler-rt/lib/rtsan/rtsan_stack.h b/compiler-rt/lib/rtsan/rtsan_stack.h new file mode 100644 index 0000000000000..cecdd43045db5 --- /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 0000000000000..87a5730685376 --- /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) + 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_target_properties(RtsanUnitTests PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + set(RTSAN_TEST_RUNTIME RTRtsanTest.${arch}) + if(APPLE) + set(RTSAN_TEST_RUNTIME_OBJECTS + $ + $ + $ + $ + $ + $) + else() + set(RTSAN_TEST_RUNTIME_OBJECTS + $ + $ + $ + $ + $ + $ + $) + 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 0000000000000..b7e73236a14cc --- /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 0000000000000..97afb1eefb640 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 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>(); + 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 &&function) { function(); } +} // namespace + +TEST(TestRtsan, CopyingALambdaWithLargeCaptureDiesWhenRealtime) { + std::array 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 small_atomic{0.0f}; + ASSERT_TRUE(small_atomic.is_lock_free()); + RealtimeInvoke([&small_atomic]() { float x = small_atomic.load(); }); + + std::atomic> 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 0000000000000..f5b016089087d --- /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 +#include + +#include "rtsan_test_utilities.h" + +#if SANITIZER_APPLE +#include +#include +#endif + +#if SANITIZER_INTERCEPT_MEMALIGN || SANITIZER_INTERCEPT_PVALLOC +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +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 0000000000000..255ac9497103e --- /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 0000000000000..6ca09cf657094 --- /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 + +namespace rtsan_testing { + +template void RealtimeInvoke(Function &&Func) { + __rtsan_realtime_enter(); + std::forward(Func)(); + __rtsan_realtime_exit(); +} + +template +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(Func)), + ExitedWithCode(EXIT_FAILURE), GetExpectedErrorSubstring()); +} + +template void ExpectNonRealtimeSurvival(Function &&Func) { + std::forward(Func)(); +} + +} // namespace rtsan_testing diff --git a/compiler-rt/test/radsan/CMakeLists.txt b/compiler-rt/test/radsan/CMakeLists.txt deleted file mode 100644 index 51a3ff72859b6..0000000000000 --- a/compiler-rt/test/radsan/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -set(RADSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -set(RADSAN_TESTSUITES) -set(RADSAN_FDR_TESTSUITES) - -set(RADSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -set(RADSAN_FDR_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -set(RADSAN_TEST_ARCH ${RADSAN_SUPPORTED_ARCH}) -if(APPLE) - darwin_filter_host_archs(RADSAN_SUPPORTED_ARCH RADSAN_TEST_ARCH) -endif() - -if (COMPILER_RT_HAS_RADSAN) - foreach(arch ${RADSAN_TEST_ARCH}) - set(RADSAN_TEST_TARGET_ARCH ${arch}) - string(TOLOWER "-${arch}-${OS_NAME}" RADSAN_TEST_CONFIG_SUFFIX) - get_test_cc_for_arch(${arch} RADSAN_TEST_TARGET_CC RADSAN_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 RADSAN_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_RADSAN_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 RADSAN_TEST_DEPS RadsanUnitTests) - list(APPEND RADSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) - if(COMPILER_RT_RADSAN_HAS_STATIC_RUNTIME) - list(APPEND RADSAN_DYNAMIC_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit/dynamic) - endif() -endif() - -add_lit_testsuite(check-radsan "Running the Radsan tests" - ${RADSAN_TESTSUITES} - DEPENDS ${RADSAN_TEST_DEPS}) -set_target_properties(check-radsan PROPERTIES FOLDER "Compiler-RT Misc") diff --git a/compiler-rt/test/radsan/lit.cfg.py b/compiler-rt/test/radsan/lit.cfg.py deleted file mode 100644 index 773b624900fa6..0000000000000 --- a/compiler-rt/test/radsan/lit.cfg.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- Python -*- - -import os - -# Setup config name. -config.name = "RADSAN" + config.name_suffix - -# Setup source root. -config.test_source_root = os.path.dirname(__file__) - -# Setup default compiler flags use with -fradsan-instrument option. -clang_radsan_cflags = ["-fradsan-instrument", config.target_cflags] - -# If libc++ was used to build radsan libraries, libc++ is needed. Fix applied -# to Linux only since -rpath may not be portable. This can be extended to -# other platforms. -if config.libcxx_used == "1" and config.host_os == "Linux": - clang_radsan_cflags = clang_radsan_cflags + ( - ["-L%s -lc++ -Wl,-rpath=%s" % (config.llvm_shlib_dir, config.llvm_shlib_dir)] - ) - -clang_radsan_cxxflags = config.cxx_mode_flags + clang_radsan_cflags - - -def build_invocation(compile_flags): - return " " + " ".join([config.clang] + compile_flags) + " " - - -# Assume that llvm-radsan is in the config.llvm_tools_dir. -llvm_radsan = os.path.join(config.llvm_tools_dir, "llvm-radsan") - -# 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_radsan ", build_invocation(clang_radsan_cflags))) -config.substitutions.append(("%clangxx_radsan", build_invocation(clang_radsan_cxxflags))) -config.substitutions.append(("%llvm_radsan", llvm_radsan)) -config.substitutions.append( - ( - "%radsanlib", - ( - "-lm -lpthread %s -lrt -L%s " - "-Wl,-whole-archive -lclang_rt.radsan%s -Wl,-no-whole-archive" - ) - % (libdl_flag, config.compiler_rt_libdir, config.target_suffix), - ) -) - -# 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/CMakeLists.txt b/compiler-rt/test/rtsan/CMakeLists.txt new file mode 100644 index 0000000000000..ba1070467f1a3 --- /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/radsan/Unit/lit.site.cfg.py.in b/compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in similarity index 65% rename from compiler-rt/test/radsan/Unit/lit.site.cfg.py.in rename to compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in index 432e0ed5e1907..59e1e10360b52 100644 --- a/compiler-rt/test/radsan/Unit/lit.site.cfg.py.in +++ b/compiler-rt/test/rtsan/Unit/lit.site.cfg.py.in @@ -9,7 +9,7 @@ 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/radsan/tests" +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/rtsan/tests" config.test_source_root = config.test_exec_root if not config.parallelism_group: @@ -17,9 +17,9 @@ if not config.parallelism_group: 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 radsan/lit.cfg.py. - if 'RADSAN_OPTIONS' in config.environment: - config.environment['RADSAN_OPTIONS'] += ':ignore_noninstrumented_modules=0' + # 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['RADSAN_OPTIONS'] = 'ignore_noninstrumented_modules=0' - config.environment['RADSAN_OPTIONS'] += ':ignore_interceptors_accesses=0' + 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 0000000000000..b262ecfa7fb4b --- /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/radsan/lit.site.cfg.py.in b/compiler-rt/test/rtsan/lit.site.cfg.py.in similarity index 69% rename from compiler-rt/test/radsan/lit.site.cfg.py.in rename to compiler-rt/test/rtsan/lit.site.cfg.py.in index 7d509c9f2eb91..5b458645e94dc 100644 --- a/compiler-rt/test/radsan/lit.site.cfg.py.in +++ b/compiler-rt/test/rtsan/lit.site.cfg.py.in @@ -1,10 +1,10 @@ @LIT_SITE_CFG_IN_HEADER@ # Tool-specific config options. -config.name_suffix = "@RADSAN_TEST_CONFIG_SUFFIX@" -config.radsan_lit_source_dir = "@RADSAN_LIT_SOURCE_DIR@" -config.target_cflags = "@RADSAN_TEST_TARGET_CFLAGS@" -config.target_arch = "@RADSAN_TEST_TARGET_ARCH@" +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: diff --git a/compiler-rt/test/sanitizer_common/CMakeLists.txt b/compiler-rt/test/sanitizer_common/CMakeLists.txt index 1c37cf2a0c721..edecc04d48c75 100644 --- a/compiler-rt/test/sanitizer_common/CMakeLists.txt +++ b/compiler-rt/test/sanitizer_common/CMakeLists.txt @@ -7,7 +7,7 @@ set(SANITIZER_COMMON_TESTSUITES) # FIXME(dliew): We should switch to COMPILER_RT_SANITIZERS_TO_BUILD instead of # the hard coded `SUPPORTED_TOOLS_INIT` list once we know that the other # sanitizers work. -set(SUPPORTED_TOOLS_INIT asan lsan hwasan msan radsan tsan ubsan) +set(SUPPORTED_TOOLS_INIT asan lsan hwasan msan tsan ubsan) set(SUPPORTED_TOOLS) foreach(SANITIZER_TOOL ${SUPPORTED_TOOLS_INIT}) string(TOUPPER ${SANITIZER_TOOL} SANITIZER_TOOL_UPPER) diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg.py b/compiler-rt/test/sanitizer_common/lit.common.cfg.py index 80c7372372f44..04af4816eb6e7 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py @@ -18,9 +18,6 @@ tool_options = "HWASAN_OPTIONS" if not config.has_lld: config.unsupported = True -elif config.tool_name == "radsan": - tool_cflags = ["-fsanitize=realtime"] - tool_options = "RADSAN_OPTIONS" elif config.tool_name == "tsan": tool_cflags = ["-fsanitize=thread"] tool_options = "TSAN_OPTIONS" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 2c8910dc26678..8d0ffd6ed725b 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -1041,6 +1041,7 @@ add_custom_command(OUTPUT "${LIBCXX_GENERATED_INCLUDE_DIR}/libcxx.imp" list(APPEND _all_includes "${LIBCXX_GENERATED_INCLUDE_DIR}/libcxx.imp") add_custom_target(generate-cxx-headers ALL DEPENDS ${_all_includes}) + add_library(cxx-headers INTERFACE) target_link_libraries(cxx-headers INTERFACE libcxx-abi-headers) add_dependencies(cxx-headers generate-cxx-headers) diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 223236248ad67..43740036d130d 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -756,7 +756,8 @@ enum AttributeKindCodes { ATTR_KIND_RANGE = 92, ATTR_KIND_SANITIZE_NUMERICAL_STABILITY = 93, ATTR_KIND_INITIALIZES = 94, - ATTR_KIND_NO_SANITIZE_REALTIME = 95, + ATTR_KIND_NONBLOCKING = 95, + ATTR_KIND_NO_SANITIZE_REALTIME = 96, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 912e7a0d94846..2a0e7542d3217 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -239,8 +239,8 @@ def ReadNone : EnumAttr<"readnone", [ParamAttr]>; /// Function only reads from memory. def ReadOnly : EnumAttr<"readonly", [ParamAttr]>; -/// Function is marked to be analyzed by the realtime sanitizer. -def Realtime : EnumAttr<"realtime", [FnAttr]>; +/// Function is marked to be non-blocking +def NonBlocking : EnumAttr<"nonblocking", [FnAttr]>; /// Return value is always equal to this argument. def Returned : EnumAttr<"returned", [ParamAttr]>; diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index d6431c3467bfa..7a57f18d8984a 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2109,8 +2109,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::ReadOnly; case bitc::ATTR_KIND_RETURNED: return Attribute::Returned; - case bitc::ATTR_KIND_REALTIME: - return Attribute::Realtime; + case bitc::ATTR_KIND_NONBLOCKING: + return Attribute::NonBlocking; case bitc::ATTR_KIND_NO_SANITIZE_REALTIME: return Attribute::NoSanitizeRealtime; case bitc::ATTR_KIND_RETURNS_TWICE: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 221bdcc996834..a74e9c48f8d8b 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -793,8 +793,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_OPTIMIZE_FOR_SIZE; case Attribute::OptimizeNone: return bitc::ATTR_KIND_OPTIMIZE_NONE; - case Attribute::Realtime: - return bitc::ATTR_KIND_REALTIME; + case Attribute::NonBlocking: + return bitc::ATTR_KIND_NONBLOCKING; case Attribute::NoSanitizeRealtime: return bitc::ATTR_KIND_NO_SANITIZE_REALTIME; case Attribute::ReadNone: diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 079062d311228..3c2f88acb8ba8 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -969,7 +969,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::MustProgress: case Attribute::NoProfile: case Attribute::SkipProfile: - case Attribute::Realtime: + case Attribute::NonBlocking: case Attribute::NoSanitizeRealtime: break; // These attributes cannot be applied to functions.