https://github.com/cjappl created https://github.com/llvm/llvm-project/pull/100192
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](https://github.com/realtime-sanitizer/radsan/blob/doc/review-support/doc/review.md) for what our next steps are. >From 0307f457862e4a7ef623a74690422eb1425f1067 Mon Sep 17 00:00:00 2001 From: Chris Apple <cja-priv...@pm.me> Date: Thu, 18 Jul 2024 17:29:01 +0200 Subject: [PATCH] [rtsan] Introduce rtsan clang codegen --- clang/include/clang/Basic/Attr.td | 5 +++ clang/include/clang/Basic/Sanitizers.def | 3 ++ clang/include/clang/Driver/SanitizerArgs.h | 1 + clang/lib/CodeGen/CodeGenFunction.cpp | 51 ++++++++++++++++++++++ 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 +++++ 10 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 clang/test/Driver/rtsan.c 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits