-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][rtsan] Introduce realtime sanitizer clang codegen and -fsanitize flag #100192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: Chris Apple (cjappl) ChangesInserts the Introduces the -fsanitize=realtime flag, and unit tests to confirm things are working. Please see the reviewer support document for what our next steps are. Full diff: https://github.com/llvm/llvm-project/pull/100192.diff 10 Files Affected:
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/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 1e98bea8c8ce3..9346218bd1ce4 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,27 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
}
}
+ if (SanOpts.has(SanitizerKind::Realtime)) {
+ for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) {
+ if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) {
+ InsertCallAtFunctionEntryPoint(Fn, "__rtsan_realtime_enter");
+ break;
+ }
+ }
+ }
+
// Emit the standard function epilogue.
FinishFunction(BodyRange.getEnd());
+ if (SanOpts.has(SanitizerKind::Realtime)) {
+ for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) {
+ if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) {
+ InsertCallAtAllFunctionExitPoints(Fn, "__rtsan_realtime_exit");
+ break;
+ }
+ }
+ }
+
// 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
|
@llvm/pr-subscribers-clang-driver Author: Chris Apple (cjappl) ChangesInserts the Introduces the -fsanitize=realtime flag, and unit tests to confirm things are working. Please see the reviewer support document for what our next steps are. Full diff: https://github.com/llvm/llvm-project/pull/100192.diff 10 Files Affected:
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/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 1e98bea8c8ce3..9346218bd1ce4 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,27 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
}
}
+ if (SanOpts.has(SanitizerKind::Realtime)) {
+ for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) {
+ if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) {
+ InsertCallAtFunctionEntryPoint(Fn, "__rtsan_realtime_enter");
+ break;
+ }
+ }
+ }
+
// Emit the standard function epilogue.
FinishFunction(BodyRange.getEnd());
+ if (SanOpts.has(SanitizerKind::Realtime)) {
+ for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) {
+ if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) {
+ InsertCallAtAllFunctionExitPoints(Fn, "__rtsan_realtime_exit");
+ break;
+ }
+ }
+ }
+
// 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
|
@@ -1410,6 +1414,35 @@ QualType CodeGenFunction::BuildFunctionArgList(GlobalDecl GD, | |||
return ResTy; | |||
} | |||
|
|||
void InsertCallBeforeInstruction(llvm::Function *Fn, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in this file require a test in llvm-project/clang/test/CodeGen/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also maybe I missed some discussion.
these changes looks like better be done by llvm pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style wise: https://llvm.org/docs/CodingStandards.html#anonymous-namespaces Use static for internal linkage functions. LLVM code is more consistent in that most functions use functionName
while clang is more inconsistent (a lot of FunctionName
). When moved to LLVM, make sure that the case is changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also maybe I missed some discussion. these changes looks like better be done by llvm pass.
Nope, this is just us not knowing what we don't know!
Any thoughts as to where this may live in that step? I'm unfamiliar with where it might be appropriate. Pointing to a general file or something to grep for would be greatly appreciated! Thanks Vitaly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add a bit of context here on some attempts we made implementing the insertion in codegen vs. in a pass.
In the very first implementation of rtsan, we did actually implement the insertion of __rtsan_realtime_enter()
and __rtsan_realtime_exit()
in a pass. We thought at the time that this insertion was only possible in a pass if the nonblocking
attribute was added to the function in its IR. This method seemed to work fine until we activated optimisation level -O1
, at which point we found that our attribute in the IR was disappearing, presumably from some sort of DCE (but we weren't sure), before we had a chance to inject our function calls. This, of course, broke our code injection. Someone on the LLVM Discord server pointed out that it was possible to do what we wanted to do entirely at CodeGen time, so we pivoted our approach towards doing so.
@vitalybuka and @MaskRay, what would you recommend here? Are the problems that we experienced originally with the pass implementation surmountable? We also questioned whether we should be injecting early (before optimisations) or late (after optimisations). Our first assumption was that early was better (less risk of inlining optimisations breaking our realtime depth tracking?), but we're very keen to learn from your expert opinions here.
Thanks to the feedback from @MaskRay and @vitalybuka , closing in favor of doing this processing in LLVM. (PR: #100596) The clang driver changes will come after that is merged! Appreciate all the reviews setting us on the right path. |
Inserts the
__rtsan_realtime_enter
at the first line of all functions with [[clang::nonblocking]] function effects, and__rtsan_realtime_exit
at all exit points.Introduces the -fsanitize=realtime flag, and unit tests to confirm things are working.
Please see the reviewer support document for what our next steps are.