From 1b4aead5e3605806fccd18e8ee7baeafe9585c3e Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Wed, 24 Jul 2024 07:32:15 -0700 Subject: [PATCH 1/3] [LLVM][rtsan] Add NonBlocking Attribute and RealtimeSanitizer pass --- llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 + llvm/include/llvm/IR/Attributes.td | 3 ++ .../Instrumentation/RealtimeSanitizer.h | 35 +++++++++++++ .../RealtimeSanitizerOptions.h | 17 +++++++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 + llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 + llvm/lib/Passes/PassBuilder.cpp | 6 +++ llvm/lib/Passes/PassRegistry.def | 4 ++ .../Transforms/Instrumentation/CMakeLists.txt | 1 + .../Instrumentation/RealtimeSanitizer.cpp | 50 +++++++++++++++++++ llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 + llvm/test/Bitcode/attributes.ll | 7 +++ llvm/test/Bitcode/compatibility.ll | 8 ++- .../RealtimeSanitizer/rtsan.ll | 35 +++++++++++++ 14 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h create mode 100644 llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h create mode 100644 llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp create mode 100644 llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll 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/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h new file mode 100644 index 0000000000000..9cf2448361608 --- /dev/null +++ b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h @@ -0,0 +1,35 @@ +//===--------- Definition of the RealtimeSanitizer class ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H +#define LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h" + +namespace llvm { + +struct RealtimeSanitizerOptions {}; + +class RealtimeSanitizerPass : public PassInfoMixin { +public: + RealtimeSanitizerPass(const RealtimeSanitizerOptions &Options); + PreservedAnalyses run(Function &F, AnalysisManager &AM); + + static bool isRequired() { return true; } + +private: + RealtimeSanitizerOptions Options{}; +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H diff --git a/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h new file mode 100644 index 0000000000000..35376a5647c60 --- /dev/null +++ b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h @@ -0,0 +1,17 @@ +//===--------- Definition of the RealtimeSanitizer options -------*- 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 +// +//===----------------------------------------------------------------------===// +// This file defines data types used to set Realtime Sanitizer options. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZEROPTIONS_H +#define LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZEROPTIONS_H + +namespace llvm {} // namespace llvm + +#endif 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/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 5dbb1e2f49871..faf3e25cb31d9 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -198,6 +198,7 @@ #include "llvm/Transforms/Instrumentation/PGOForceFunctionAttrs.h" #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" #include "llvm/Transforms/Instrumentation/PoisonChecking.h" +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" @@ -1207,6 +1208,11 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) { return Opts; } +Expected parseRtSanPassOptions(StringRef Params) { + RealtimeSanitizerOptions Result; + return Result; +} + } // namespace /// Tests whether a pass name starts with a valid prefix for a default pipeline diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 3b92823cd283b..f795c7242f958 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -590,6 +590,10 @@ FUNCTION_PASS_WITH_PARAMS( return WinEHPreparePass(DemoteCatchSwitchPHIOnly); }, parseWinEHPrepareOptions, "demote-catchswitch-only") +FUNCTION_PASS_WITH_PARAMS( + "rtsan", "RealtimeSanitizerPass", + [](RealtimeSanitizerOptions Opts) { return RealtimeSanitizerPass(Opts); }, + parseRtSanPassOptions, "") #undef FUNCTION_PASS_WITH_PARAMS #ifndef LOOPNEST_PASS diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt index 4e3f9e27e0c34..deab37801ff1d 100644 --- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -25,6 +25,7 @@ add_llvm_component_library(LLVMInstrumentation ValueProfileCollector.cpp ThreadSanitizer.cpp HWAddressSanitizer.cpp + RealtimeSanitizer.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp new file mode 100644 index 0000000000000..2fa2389c4984f --- /dev/null +++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp @@ -0,0 +1,50 @@ +#include "llvm/IR/Analysis.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" + +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" + +using namespace llvm; + +namespace { + +void insertCallBeforeInstruction(Function &Fn, Instruction &Instruction, + const char *FunctionName) { + LLVMContext &Context = Fn.getContext(); + FunctionType *FuncType = FunctionType::get(Type::getVoidTy(Context), false); + FunctionCallee Func = + Fn.getParent()->getOrInsertFunction(FunctionName, FuncType); + IRBuilder<> Builder{&Instruction}; + Builder.CreateCall(Func, {}); +} + +void insertCallAtFunctionEntryPoint(Function &Fn, const char *InsertFnName) { + + insertCallBeforeInstruction(Fn, Fn.front().front(), InsertFnName); +} + +void insertCallAtAllFunctionExitPoints(Function &Fn, const char *InsertFnName) { + for (auto &BB : Fn) { + for (auto &I : BB) { + if (auto *RI = dyn_cast(&I)) { + insertCallBeforeInstruction(Fn, I, InsertFnName); + } + } + } +} +} // namespace + +RealtimeSanitizerPass::RealtimeSanitizerPass( + const RealtimeSanitizerOptions &Options) + : Options{Options} {} + +PreservedAnalyses RealtimeSanitizerPass::run(Function &F, + AnalysisManager &AM) { + if (F.hasFnAttribute(Attribute::NonBlocking)) { + insertCallAtFunctionEntryPoint(F, "__rtsan_realtime_enter"); + insertCallAtAllFunctionExitPoints(F, "__rtsan_realtime_exit"); + return PreservedAnalyses::none(); + } + + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 5bca5cf8ff91f..1339bbb3842d4 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -946,6 +946,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::NoUnwind: case Attribute::NoSanitizeBounds: case Attribute::NoSanitizeCoverage: + case Attribute::NonBlocking: case Attribute::NullPointerIsValid: case Attribute::OptimizeForDebugging: case Attribute::OptForFuzzing: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll index f4dc9b9849827..5f8617dad8dfd 100644 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -505,6 +505,12 @@ define void @f86() nosanitize_bounds ret void; } +; CHECK: define void @f92() #53 +define void @f92() nonblocking +{ + ret void; +} + ; CHECK: define void @f87() [[FNRETTHUNKEXTERN:#[0-9]+]] define void @f87() fn_ret_thunk_extern { ret void } @@ -599,6 +605,7 @@ define void @initializes(ptr initializes((-4, 0), (4, 8)) %a) { ; CHECK: attributes #50 = { disable_sanitizer_instrumentation } ; CHECK: attributes #51 = { uwtable(sync) } ; CHECK: attributes #52 = { nosanitize_bounds } +; CHECK: attributes #53 = { nonblocking } ; CHECK: attributes [[FNRETTHUNKEXTERN]] = { fn_ret_thunk_extern } ; CHECK: attributes [[SKIPPROFILE]] = { skipprofile } ; CHECK: attributes [[OPTDEBUG]] = { optdebug } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll index e437c37d8d1c8..1471138b91a9f 100644 --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -1564,7 +1564,7 @@ exit: ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #52 + ; CHECK: call void @f.nobuiltin() #53 call fastcc noalias ptr @f.noalias() noinline ; CHECK: call fastcc noalias ptr @f.noalias() #12 @@ -1991,6 +1991,9 @@ declare void @f.allockind() allockind("alloc,uninitialized") declare void @f.sanitize_numerical_stability() sanitize_numerical_stability ; CHECK: declare void @f.sanitize_numerical_stability() #51 +declare void @f.nonblocking() nonblocking +; CHECK: declare void @f.nonblocking() #52 + ; CHECK: declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan)) declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan)) @@ -2113,7 +2116,8 @@ define float @nofpclass_callsites(float %arg) { ; CHECK: attributes #49 = { nosanitize_bounds } ; CHECK: attributes #50 = { allockind("alloc,uninitialized") } ; CHECK: attributes #51 = { sanitize_numerical_stability } -; CHECK: attributes #52 = { builtin } +; CHECK: attributes #52 = { nonblocking } +; CHECK: attributes #53 = { builtin } ;; Metadata diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll new file mode 100644 index 0000000000000..222df484273c7 --- /dev/null +++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll @@ -0,0 +1,35 @@ +; RUN: opt < %s -passes=rtsan -S | FileCheck %s + +; Function Attrs: mustprogress noinline nonblocking optnone ssp uwtable(sync) +define void @violation() #0 { + %1 = alloca ptr, align 8 + %2 = call ptr @malloc(i64 noundef 2) #3 + store ptr %2, ptr %1, align 8 + ret void +} + +; Function Attrs: allocsize(0) +declare ptr @malloc(i64 noundef) #1 + +; Function Attrs: mustprogress noinline norecurse optnone ssp uwtable(sync) +define noundef i32 @main() #2 { + %1 = alloca i32, align 4 + store i32 0, ptr %1, align 4 + call void @violation() #4 + ret i32 0 +} + +attributes #0 = { mustprogress noinline nonblocking optnone ssp uwtable(sync) } +attributes #1 = { allocsize(0) } +attributes #2 = { mustprogress noinline norecurse optnone ssp uwtable(sync) } +attributes #3 = { allocsize(0) } +attributes #4 = { nonblocking } + +; RealtimeSanitizer pass should insert __rtsan_realtime_enter right after function definition +; CHECK: define{{.*}}violation +; CHECK-NEXT: call{{.*}}__rtsan_realtime_enter + +; RealtimeSanitizer pass should insert __rtsan_realtime_exit right before function return +; CHECK: call{{.*}}@__rtsan_realtime_exit +; CHECK-NEXT: ret{{.*}}void + From e876f41ec71ce41200c9fd3a84a60ff918fd99ca Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Wed, 24 Jul 2024 14:25:44 -0700 Subject: [PATCH 2/3] [clang][rtsan] Introduce realtime sanitizer codegen and driver --- clang/include/clang/Basic/Sanitizers.def | 3 +++ clang/include/clang/Driver/SanitizerArgs.h | 1 + clang/lib/CodeGen/BackendUtil.cpp | 8 ++++++++ clang/lib/CodeGen/CGCall.cpp | 6 ++++++ 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/CodeGen/rtsan_insert_at_entry.c | 9 +++++++++ clang/test/CodeGen/rtsan_insert_at_exit.c | 9 +++++++++ .../CodeGen/rtsan_nonblocking_attribute_inserted.c | 7 +++++++ clang/test/Driver/rtsan.c | 14 ++++++++++++++ 12 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 clang/test/CodeGen/rtsan_insert_at_entry.c create mode 100644 clang/test/CodeGen/rtsan_insert_at_exit.c create mode 100644 clang/test/CodeGen/rtsan_nonblocking_attribute_inserted.c create mode 100644 clang/test/Driver/rtsan.c 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/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index e765bbf637a66..abf45e07a8e58 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -78,6 +78,7 @@ #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #include "llvm/Transforms/Instrumentation/NumericalStabilitySanitizer.h" #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" @@ -995,6 +996,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline( FPM.addPass(BoundsCheckingPass()); }); + if (LangOpts.Sanitize.has(SanitizerKind::Realtime)) { + PB.registerScalarOptimizerLateEPCallback( + [](FunctionPassManager &FPM, OptimizationLevel Level) { + RealtimeSanitizerOptions Opts; + FPM.addPass(RealtimeSanitizerPass(Opts)); + }); + } // Don't add sanitizers if we are here from ThinLTO PostLink. That already // done on PreLink stage. if (!IsThinLTOPostLink) { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 2f3dd5d01fa6c..7aa82b85c4662 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2411,6 +2411,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/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 940ea3de492b1..428954a7290d6 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1408,6 +1408,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. @@ -1433,6 +1435,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 17b6074160716..d58aa79fe20c9 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()) { @@ -3506,6 +3513,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/CodeGen/rtsan_insert_at_entry.c b/clang/test/CodeGen/rtsan_insert_at_entry.c new file mode 100644 index 0000000000000..2640ff199e2d2 --- /dev/null +++ b/clang/test/CodeGen/rtsan_insert_at_entry.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s + +// The first instruction after the function is entred should be a call to +// enable the realtime sanitizer stack + +int foo(int *a) [[clang::nonblocking]] { return *a; } +// CHECK: define{{.*}}foo +// CHECK-NEXT: entry: +// CHECK-NEXT: call{{.*}}__rtsan_realtime_enter diff --git a/clang/test/CodeGen/rtsan_insert_at_exit.c b/clang/test/CodeGen/rtsan_insert_at_exit.c new file mode 100644 index 0000000000000..2fb0749492b56 --- /dev/null +++ b/clang/test/CodeGen/rtsan_insert_at_exit.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s + +// __rtsan_realtime_exit should be inserted at all function returns + +int bar(int* x) [[clang::nonblocking]] { + return *x; +} +// CHECK: call{{.*}}__rtsan_realtime_exit +// CHECK-NEXT: ret diff --git a/clang/test/CodeGen/rtsan_nonblocking_attribute_inserted.c b/clang/test/CodeGen/rtsan_nonblocking_attribute_inserted.c new file mode 100644 index 0000000000000..a984ad3a77cfb --- /dev/null +++ b/clang/test/CodeGen/rtsan_nonblocking_attribute_inserted.c @@ -0,0 +1,7 @@ +// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s + +float process(float *a) [[clang::nonblocking]] { return *a; } + +// CHECK: define{{.*}}process{{.*}}#0 { +// CHECK: attributes #0 = { +// CHECK-SAME: {{.*nonblocking.*}} diff --git a/clang/test/Driver/rtsan.c b/clang/test/Driver/rtsan.c new file mode 100644 index 0000000000000..999e88d066427 --- /dev/null +++ b/clang/test/Driver/rtsan.c @@ -0,0 +1,14 @@ +// 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 + +// Ensure the rtsan_realtime calls are never optimized away + +int foo(int *a) [[clang::nonblocking]] { return *a; } +// CHECK: __rtsan_realtime_enter +// CHECK: __rtsan_realtime_exit From e805be2bb39ce5f02a2eabdb5f5fb9131a55efff Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Thu, 18 Jul 2024 17:29:01 +0200 Subject: [PATCH 3/3] [compiler-rt][rtsan] Introduce RTsan lit tests --- 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/lit.common.cfg.py | 3 +++ 6 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 compiler-rt/test/rtsan/test_rtsan.cpp create mode 100644 compiler-rt/test/rtsan/test_rtsan_inactive.cpp 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 { } // 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 + +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 +#include + +// 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/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"