[clang] [compiler-rt] [llvm] [clang][llvm][rtsan] Introduce RealtimeSanitizer clang codegen, llvm attributes (PR #100120)

Chris Apple via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 23 08:49:40 PDT 2024


https://github.com/cjappl updated https://github.com/llvm/llvm-project/pull/100120

>From e0a1336da28f0f1d9f870be1676991d415160321 Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
Date: Thu, 18 Jul 2024 17:29:01 +0200
Subject: [PATCH 1/2] [rtsan] Introduce rtsan frontend

---
 clang/include/clang/Basic/Attr.td             |  5 ++
 clang/include/clang/Basic/Sanitizers.def      |  3 ++
 clang/include/clang/Driver/SanitizerArgs.h    |  1 +
 clang/lib/CodeGen/CGCall.cpp                  |  6 +++
 clang/lib/CodeGen/CodeGenFunction.cpp         | 43 +++++++++++++++++
 clang/lib/Driver/SanitizerArgs.cpp            | 14 ++++--
 clang/lib/Driver/ToolChains/CommonArgs.cpp    |  7 +++
 clang/lib/Driver/ToolChains/Darwin.cpp        |  8 ++++
 clang/lib/Driver/ToolChains/Linux.cpp         |  1 +
 clang/test/Driver/fsanitize.c                 | 48 +++++++++++++++++++
 clang/test/Driver/rtsan.c                     | 12 +++++
 compiler-rt/lib/rtsan/rtsan_interceptors.cpp  |  4 +-
 compiler-rt/lib/rtsan/tests/CMakeLists.txt    | 15 +++---
 compiler-rt/test/rtsan/CMakeLists.txt         | 11 -----
 compiler-rt/test/rtsan/test_rtsan.cpp         | 17 +++++++
 .../test/rtsan/test_rtsan_inactive.cpp        | 23 +++++++++
 .../test/sanitizer_common/CMakeLists.txt      |  2 +-
 .../test/sanitizer_common/lit.common.cfg.py   |  3 ++
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |  1 +
 llvm/include/llvm/IR/Attributes.td            |  3 ++
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |  2 +
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |  2 +
 llvm/lib/Transforms/Utils/CodeExtractor.cpp   |  1 +
 23 files changed, 206 insertions(+), 26 deletions(-)
 create mode 100644 clang/test/Driver/rtsan.c
 create mode 100644 compiler-rt/test/rtsan/test_rtsan.cpp
 create mode 100644 compiler-rt/test/rtsan/test_rtsan_inactive.cpp

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 4825979a974d2..8a4a29d589b9b 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3527,6 +3527,11 @@ def NoSanitize : InheritableAttr {
     bool hasCoverage() const {
       return llvm::is_contained(sanitizers(), "coverage");
     }
+
+    bool hasRealtime() const {
+      return llvm::is_contained(sanitizers(), "realtime");
+    }
+
   }];
 }
 
diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def
index bee35e9dca7c3..8a5df643ffa0c 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -37,6 +37,9 @@
 #endif
 
 
+// RealtimeSanitizer
+SANITIZER("realtime", Realtime)
+
 // AddressSanitizer
 SANITIZER("address", Address)
 
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 47ef175302679..6c0df926aec5b 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -79,6 +79,7 @@ class SanitizerArgs {
   bool needsStableAbi() const { return StableABI; }
 
   bool needsMemProfRt() const { return NeedsMemProfRt; }
+  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 d582aba679ddc..7af7785515688 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2408,6 +2408,12 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
           FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
         NBA = Fn->getAttr<NoBuiltinAttr>();
       }
+
+      for (const FunctionEffectWithCondition &Fe : Fn->getFunctionEffects()) {
+        if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) {
+          FuncAttrs.addAttribute(llvm::Attribute::NonBlocking);
+        }
+      }
     }
 
     if (isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl)) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 1e98bea8c8ce3..84ce2b20dd45d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -31,6 +31,7 @@
 #include "clang/AST/StmtObjC.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/Sanitizers.h"
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CodeGen/CGFunctionInfo.h"
@@ -40,6 +41,9 @@
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/FPEnv.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/MDBuilder.h"
@@ -1410,6 +1414,35 @@ QualType CodeGenFunction::BuildFunctionArgList(GlobalDecl GD,
   return ResTy;
 }
 
+void InsertCallBeforeInstruction(llvm::Function *Fn,
+                                 llvm::Instruction &Instruction,
+                                 const char *FunctionName) {
+  llvm::LLVMContext &context = Fn->getContext();
+  llvm::FunctionType *FuncType =
+      llvm::FunctionType::get(llvm::Type::getVoidTy(context), false);
+  llvm::FunctionCallee Func =
+      Fn->getParent()->getOrInsertFunction(FunctionName, FuncType);
+  llvm::IRBuilder<> builder{&Instruction};
+  builder.CreateCall(Func, {});
+}
+
+void InsertCallAtFunctionEntryPoint(llvm::Function *Fn,
+                                    const char *InsertFnName) {
+
+  InsertCallBeforeInstruction(Fn, Fn->front().front(), InsertFnName);
+}
+
+void InsertCallAtAllFunctionExitPoints(llvm::Function *Fn,
+                                       const char *InsertFnName) {
+  for (auto &BB : *Fn) {
+    for (auto &I : BB) {
+      if (auto *RI = dyn_cast<llvm::ReturnInst>(&I)) {
+        InsertCallBeforeInstruction(Fn, I, InsertFnName);
+      }
+    }
+  }
+}
+
 void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
                                    const CGFunctionInfo &FnInfo) {
   assert(Fn && "generating code for null Function");
@@ -1578,9 +1611,19 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
     }
   }
 
+  if (SanOpts.has(SanitizerKind::Realtime)) {
+    if (Fn->hasFnAttribute(llvm::Attribute::NonBlocking))
+      InsertCallAtFunctionEntryPoint(Fn, "__rtsan_realtime_enter");
+  }
+
   // Emit the standard function epilogue.
   FinishFunction(BodyRange.getEnd());
 
+  if (SanOpts.has(SanitizerKind::Realtime)) {
+    if (Fn->hasFnAttribute(llvm::Attribute::NonBlocking))
+      InsertCallAtAllFunctionExitPoints(Fn, "__rtsan_realtime_exit");
+  }
+
   // If we haven't marked the function nothrow through other means, do
   // a quick pass now to see if we can.
   if (!CurFn->doesNotThrow())
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 1fd870b72286e..7b38f20fc8d05 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -552,11 +552,15 @@ 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 019df16a909f4..5c2040fdee8d7 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1430,6 +1430,8 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
       if (!Args.hasArg(options::OPT_shared))
         HelperStaticRuntimes.push_back("hwasan-preinit");
     }
+    if (SanArgs.needsRtsanRt() && SanArgs.linkRuntimes())
+      SharedRuntimes.push_back("rtsan");
   }
 
   // The stats_client library is also statically linked into DSOs.
@@ -1455,6 +1457,11 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
       StaticRuntimes.push_back("asan_cxx");
   }
 
+  if (!SanArgs.needsSharedRt() && SanArgs.needsRtsanRt() &&
+      SanArgs.linkRuntimes()) {
+    StaticRuntimes.push_back("rtsan");
+  }
+
   if (!SanArgs.needsSharedRt() && SanArgs.needsMemProfRt()) {
     StaticRuntimes.push_back("memprof");
     if (SanArgs.linkCXXRuntimes())
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
index c6f9d7beffb1d..28e735795cb2b 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -1519,6 +1519,8 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
     const char *sanitizer = nullptr;
     if (Sanitize.needsUbsanRt()) {
       sanitizer = "UndefinedBehaviorSanitizer";
+    } else if (Sanitize.needsRtsanRt()) {
+      sanitizer = "RealtimeSanitizer";
     } else if (Sanitize.needsAsanRt()) {
       sanitizer = "AddressSanitizer";
     } else if (Sanitize.needsTsanRt()) {
@@ -1541,6 +1543,11 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
         AddLinkSanitizerLibArgs(Args, CmdArgs, "asan");
       }
     }
+    if (Sanitize.needsRtsanRt()) {
+      assert(Sanitize.needsSharedRt() &&
+             "Static sanitizer runtimes not supported");
+      AddLinkSanitizerLibArgs(Args, CmdArgs, "rtsan");
+    }
     if (Sanitize.needsLsanRt())
       AddLinkSanitizerLibArgs(Args, CmdArgs, "lsan");
     if (Sanitize.needsUbsanRt()) {
@@ -3477,6 +3484,7 @@ SanitizerMask Darwin::getSupportedSanitizers() const {
   const bool IsAArch64 = getTriple().getArch() == llvm::Triple::aarch64;
   SanitizerMask Res = ToolChain::getSupportedSanitizers();
   Res |= SanitizerKind::Address;
+  Res |= SanitizerKind::Realtime;
   Res |= SanitizerKind::PointerCompare;
   Res |= SanitizerKind::PointerSubtract;
   Res |= SanitizerKind::Leak;
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index 2265138edbffb..aa8a219dc9745 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -798,6 +798,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
   const bool IsHexagon = getTriple().getArch() == llvm::Triple::hexagon;
   SanitizerMask Res = ToolChain::getSupportedSanitizers();
   Res |= SanitizerKind::Address;
+  Res |= SanitizerKind::Realtime;
   Res |= SanitizerKind::PointerCompare;
   Res |= SanitizerKind::PointerSubtract;
   Res |= SanitizerKind::Fuzzer;
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index db14f6e195c64..c3ce8b5ae8b72 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -1038,3 +1038,51 @@
 // 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/clang/test/Driver/rtsan.c b/clang/test/Driver/rtsan.c
new file mode 100644
index 0000000000000..1ccf52d34873d
--- /dev/null
+++ b/clang/test/Driver/rtsan.c
@@ -0,0 +1,12 @@
+// RUN: %clang     -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s
+// RUN: %clang -O1 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s
+// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s
+// RUN: %clang -O3 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s
+// RUN: %clang     -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto=thin -o - | FileCheck %s
+// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto=thin -o - | FileCheck %s
+// RUN: %clang     -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto -o - | FileCheck %s
+// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto -o - | FileCheck %s
+
+int foo(int *a) [[clang::nonblocking]] { return *a; }
+// CHECK: __rtsan_realtime_enter
+// CHECK: __rtsan_realtime_exit
diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors.cpp
index 4d5423ec629d2..833062c3de41e 100644
--- a/compiler-rt/lib/rtsan/rtsan_interceptors.cpp
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors.cpp
@@ -39,7 +39,6 @@
 
 using namespace __sanitizer;
 
-using __rtsan::rtsan_init_is_running;
 using __rtsan::rtsan_initialized;
 
 namespace {
@@ -49,6 +48,9 @@ struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
 } // namespace
 
 void ExpectNotRealtime(const char *intercepted_function_name) {
+  if (!rtsan_initialized)
+    __rtsan_init();
+
   __rtsan::GetContextForThisThread().ExpectNotRealtime(
       intercepted_function_name);
 }
diff --git a/compiler-rt/lib/rtsan/tests/CMakeLists.txt b/compiler-rt/lib/rtsan/tests/CMakeLists.txt
index 3b783c90c2658..0320bbad59218 100644
--- a/compiler-rt/lib/rtsan/tests/CMakeLists.txt
+++ b/compiler-rt/lib/rtsan/tests/CMakeLists.txt
@@ -60,14 +60,13 @@ endif()
 foreach(arch ${RTSAN_TEST_ARCH})
   set(RtsanTestObjects)
 
-  # TODO: Re-enable once -fsanitize=realtime exists in clang driver
-  #generate_compiler_rt_tests(RtsanTestObjects
-  #  RtsanUnitTests "Rtsan-${arch}-Test" ${arch}
-  #  COMPILE_DEPS ${RTSAN_UNITTEST_HEADERS}
-  #  SOURCES ${RTSAN_INST_TEST_SOURCES} ${COMPILER_RT_GOOGLETEST_SOURCES}
-  #  DEPS rtsan
-  #  CFLAGS ${RTSAN_UNITTEST_CFLAGS} -fsanitize=realtime
-  #  LINK_FLAGS ${RTSAN_UNITTEST_LINK_FLAGS} -fsanitize=realtime)
+  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 rtsan
+    CFLAGS ${RTSAN_UNITTEST_CFLAGS} -fsanitize=realtime
+    LINK_FLAGS ${RTSAN_UNITTEST_LINK_FLAGS} -fsanitize=realtime)
 
   set(RTSAN_TEST_RUNTIME RTRtsanTest.${arch})
   if(APPLE)
diff --git a/compiler-rt/test/rtsan/CMakeLists.txt b/compiler-rt/test/rtsan/CMakeLists.txt
index e1f9eb39408dc..59fc5a29703fe 100644
--- a/compiler-rt/test/rtsan/CMakeLists.txt
+++ b/compiler-rt/test/rtsan/CMakeLists.txt
@@ -1,14 +1,3 @@
-
-
-
-
-######
-# 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)
diff --git a/compiler-rt/test/rtsan/test_rtsan.cpp b/compiler-rt/test/rtsan/test_rtsan.cpp
new file mode 100644
index 0000000000000..101aadc56e960
--- /dev/null
+++ b/compiler-rt/test/rtsan/test_rtsan.cpp
@@ -0,0 +1,17 @@
+// RUN: %clangxx -fsanitize=realtime %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: ios
+
+// Intent: Ensure that an intercepted call in a [[clang::nonblocking]] function
+//         is flagged as an error. Basic smoke test.
+
+#include <stdlib.h>
+
+void violation() [[clang::nonblocking]] { void *Ptr = malloc(2); }
+
+int main() {
+  violation();
+  return 0;
+  // CHECK: {{.*Real-time violation.*}}
+  // CHECK: {{.*malloc*}}
+}
diff --git a/compiler-rt/test/rtsan/test_rtsan_inactive.cpp b/compiler-rt/test/rtsan/test_rtsan_inactive.cpp
new file mode 100644
index 0000000000000..86907df6dfa16
--- /dev/null
+++ b/compiler-rt/test/rtsan/test_rtsan_inactive.cpp
@@ -0,0 +1,23 @@
+// RUN: %clangxx %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: ios
+
+// Intent: Ensure [[clang::nonblocking]] has no impact if -fsanitize=realtime is not used
+
+#include <stdio.h>
+#include <stdlib.h>
+
+// In this test, we don't use the -fsanitize=realtime flag, so nothing
+// should happen here
+void violation() [[clang::nonblocking]] { void *Ptr = malloc(2); }
+
+int main() {
+  printf("Starting run\n");
+  violation();
+  printf("No violations ended the program\n");
+  return 0;
+  // CHECK: {{.*Starting run.*}}
+  // CHECK NOT: {{.*Real-time violation.*}}
+  // CHECK NOT: {{.*malloc*}}
+  // CHECK: {{.*No violations ended the program.*}}
+}
diff --git a/compiler-rt/test/sanitizer_common/CMakeLists.txt b/compiler-rt/test/sanitizer_common/CMakeLists.txt
index fa06b82acebd9..90bb55db3d1a6 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 tsan ubsan)
+set(SUPPORTED_TOOLS_INIT asan lsan hwasan msan rtsan 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 04af4816eb6e7..5406e8838f2fc 100644
--- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py
+++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py
@@ -18,6 +18,9 @@
     tool_options = "HWASAN_OPTIONS"
     if not config.has_lld:
         config.unsupported = True
+elif config.tool_name == "rtsan":
+    tool_cflags = ["-fsanitize=realtime"]
+    tool_options = "RTSAN_OPTIONS"
 elif config.tool_name == "tsan":
     tool_cflags = ["-fsanitize=thread"]
     tool_options = "TSAN_OPTIONS"
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index fb88f2fe75adb..61b046c26a97f 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -758,6 +758,7 @@ enum AttributeKindCodes {
   ATTR_KIND_SANITIZE_NUMERICAL_STABILITY = 93,
   ATTR_KIND_INITIALIZES = 94,
   ATTR_KIND_HYBRID_PATCHABLE = 95,
+  ATTR_KIND_NONBLOCKING = 96,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index e1bd193891c1e..8d90e912f279c 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -239,6 +239,9 @@ def ReadNone : EnumAttr<"readnone", [ParamAttr]>;
 /// Function only reads from memory.
 def ReadOnly : EnumAttr<"readonly", [ParamAttr]>;
 
+/// 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 84d624f6cf8fa..a519748bc1c83 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2109,6 +2109,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::ReadOnly;
   case bitc::ATTR_KIND_RETURNED:
     return Attribute::Returned;
+  case bitc::ATTR_KIND_NONBLOCKING:
+    return Attribute::NonBlocking;
   case bitc::ATTR_KIND_RETURNS_TWICE:
     return Attribute::ReturnsTwice;
   case bitc::ATTR_KIND_S_EXT:
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 324dcbca8137e..d54aeae74a59f 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -805,6 +805,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::NonBlocking:
+    return bitc::ATTR_KIND_NONBLOCKING;
   case Attribute::ReadNone:
     return bitc::ATTR_KIND_READ_NONE;
   case Attribute::ReadOnly:
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 5bca5cf8ff91f..913c0f844f833 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -970,6 +970,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
       case Attribute::MustProgress:
       case Attribute::NoProfile:
       case Attribute::SkipProfile:
+      case Attribute::NonBlocking:
         break;
       // These attributes cannot be applied to functions.
       case Attribute::Alignment:

>From e7cce538fe2dae3c22f648d7f1e9d143b137166a Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
Date: Tue, 23 Jul 2024 08:49:18 -0700
Subject: [PATCH 2/2] Remove rtsan from SanitizerCommonTests

---
 compiler-rt/test/sanitizer_common/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler-rt/test/sanitizer_common/CMakeLists.txt b/compiler-rt/test/sanitizer_common/CMakeLists.txt
index 90bb55db3d1a6..fa06b82acebd9 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 rtsan 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)



More information about the llvm-commits mailing list