diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -314,14 +314,19 @@ static_cast(Builder); const CodeGenOptions &CGOpts = BuilderWrapper.getCGOpts(); bool Recover = CGOpts.SanitizeRecover.has(SanitizerKind::HWAddress); - PM.add( - createHWAddressSanitizerLegacyPassPass(/*CompileKernel*/ false, Recover)); + PM.add(createHWAddressSanitizerLegacyPassPass( + /*CompileKernel*/ false, Recover, + /*DisableOptimization*/ CGOpts.OptimizationLevel == 0)); } static void addKernelHWAddressSanitizerPasses(const PassManagerBuilder &Builder, - legacy::PassManagerBase &PM) { + legacy::PassManagerBase &PM) { + const PassManagerBuilderWrapper &BuilderWrapper = + static_cast(Builder); + const CodeGenOptions &CGOpts = BuilderWrapper.getCGOpts(); PM.add(createHWAddressSanitizerLegacyPassPass( - /*CompileKernel*/ true, /*Recover*/ true)); + /*CompileKernel*/ true, /*Recover*/ true, + /*DisableOptimization*/ CGOpts.OptimizationLevel == 0)); } static void addGeneralOptsForMemorySanitizer(const PassManagerBuilder &Builder, @@ -1164,7 +1169,9 @@ auto HWASanPass = [&](SanitizerMask Mask, bool CompileKernel) { if (LangOpts.Sanitize.has(Mask)) { bool Recover = CodeGenOpts.SanitizeRecover.has(Mask); - MPM.addPass(HWAddressSanitizerPass(CompileKernel, Recover)); + MPM.addPass(HWAddressSanitizerPass( + CompileKernel, Recover, + /*DisableOptimization=*/CodeGenOpts.OptimizationLevel == 0)); } }; HWASanPass(SanitizerKind::HWAddress, false); diff --git a/clang/test/CodeGen/hwasan-stack-safety-analysis.c b/clang/test/CodeGen/hwasan-stack-safety-analysis.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/hwasan-stack-safety-analysis.c @@ -0,0 +1,15 @@ +// RUN: %clang -fsanitize=hwaddress -target aarch64-linux-gnu -S -emit-llvm -mllvm -hwasan-use-stack-safety=true -mllvm -hwasan-generate-tags-with-calls -O2 %s -o - | FileCheck %s --check-prefix=SAFETY +// RUN: %clang -fsanitize=hwaddress -target aarch64-linux-gnu -S -emit-llvm -mllvm -hwasan-use-stack-safety=false -mllvm -hwasan-generate-tags-with-calls -O2 %s -o - | FileCheck %s --check-prefix=NOSAFETY + +// Default when optimizing, but not with O0. +// RUN: %clang -fsanitize=hwaddress -target aarch64-linux-gnu -S -emit-llvm -mllvm -hwasan-generate-tags-with-calls -O2 %s -o - | FileCheck %s --check-prefix=SAFETY +// RUN: %clang -fsanitize=hwaddress -target aarch64-linux-gnu -S -emit-llvm -mllvm -hwasan-generate-tags-with-calls -O0 %s -o - | FileCheck %s --check-prefix=NOSAFETY + +int main(int argc, char **argv) { + char buf[10]; + volatile char *x = buf; + *x = 0; + return buf[0]; + // NOSAFETY: __hwasan_generate_tag + // SAFETY-NOT: __hwasan_generate_tag +} diff --git a/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h --- a/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h +++ b/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h @@ -25,17 +25,21 @@ class HWAddressSanitizerPass : public PassInfoMixin { public: explicit HWAddressSanitizerPass(bool CompileKernel = false, - bool Recover = false); + bool Recover = false, + bool DisableOptimization = false); PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); static bool isRequired() { return true; } private: bool CompileKernel; bool Recover; + bool DisableOptimization; }; -FunctionPass *createHWAddressSanitizerLegacyPassPass(bool CompileKernel = false, - bool Recover = false); +FunctionPass * +createHWAddressSanitizerLegacyPassPass(bool CompileKernel = false, + bool Recover = false, + bool DisableOptimization = false); namespace HWASanAccessInfo { diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" +#include "llvm/Analysis/StackSafetyAnalysis.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" @@ -109,6 +110,11 @@ cl::desc("instrument stack (allocas)"), cl::Hidden, cl::init(true)); +static cl::opt + ClUseStackSafety("hwasan-use-stack-safety", cl::Hidden, cl::init(true), + cl::Hidden, cl::desc("Use Stack Safety analysis results"), + cl::Optional); + static cl::opt ClUARRetagToZero( "hwasan-uar-retag-to-zero", cl::desc("Clear alloca tags before returning from the function to allow " @@ -204,11 +210,23 @@ return ClInstrumentWithCalls || TargetTriple.getArch() == Triple::x86_64; } +bool mightUseStackSafetyAnalysis(bool DisableOptimization) { + return ClUseStackSafety.getNumOccurrences() ? ClUseStackSafety + : !DisableOptimization; +} + +bool shouldUseStackSafetyAnalysis(const Triple &TargetTriple, + bool DisableOptimization) { + return shouldInstrumentStack(TargetTriple) && + mightUseStackSafetyAnalysis(DisableOptimization); +} /// An instrumentation pass implementing detection of addressability bugs /// using tagged pointers. class HWAddressSanitizer { public: - HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover) : M(M) { + HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover, + const StackSafetyGlobalInfo *SSI) + : M(M), SSI(SSI) { this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0 ? ClEnableKhwasan @@ -217,6 +235,8 @@ initializeModule(); } + void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; } + bool sanitizeFunction(Function &F); void initializeModule(); void createHwasanCtorComdat(); @@ -269,6 +289,7 @@ private: LLVMContext *C; Module &M; + const StackSafetyGlobalInfo *SSI; Triple TargetTriple; FunctionCallee HWAsanMemmove, HWAsanMemcpy, HWAsanMemset; FunctionCallee HWAsanHandleVfork; @@ -339,7 +360,8 @@ static char ID; explicit HWAddressSanitizerLegacyPass(bool CompileKernel = false, - bool Recover = false) + bool Recover = false, + bool DisableOptimization = false) : FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover) { initializeHWAddressSanitizerLegacyPassPass( *PassRegistry::getPassRegistry()); @@ -348,11 +370,19 @@ StringRef getPassName() const override { return "HWAddressSanitizer"; } bool doInitialization(Module &M) override { - HWASan = std::make_unique(M, CompileKernel, Recover); + HWASan = std::make_unique(M, CompileKernel, Recover, + /*SSI=*/nullptr); return true; } bool runOnFunction(Function &F) override { + if (shouldUseStackSafetyAnalysis(Triple(F.getParent()->getTargetTriple()), + DisableOptimization)) { + // We cannot call getAnalysis in doInitialization, that would cause a + // crash as the required analyses are not initialized yet. + HWASan->setSSI( + &getAnalysis().getResult()); + } return HWASan->sanitizeFunction(F); } @@ -361,10 +391,20 @@ return false; } + void getAnalysisUsage(AnalysisUsage &AU) const override { + // This is an over-estimation of, in case we are building for an + // architecture that doesn't allow stack tagging we will still load the + // analysis. + // This is so we don't need to plumb TargetTriple all the way to here. + if (mightUseStackSafetyAnalysis(DisableOptimization)) + AU.addRequired(); + } + private: std::unique_ptr HWASan; bool CompileKernel; bool Recover; + bool DisableOptimization; }; } // end anonymous namespace @@ -375,23 +415,32 @@ HWAddressSanitizerLegacyPass, "hwasan", "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) +INITIALIZE_PASS_DEPENDENCY(StackSafetyGlobalInfoWrapperPass) INITIALIZE_PASS_END( HWAddressSanitizerLegacyPass, "hwasan", "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) -FunctionPass *llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, - bool Recover) { +FunctionPass * +llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, bool Recover, + bool DisableOptimization) { assert(!CompileKernel || Recover); - return new HWAddressSanitizerLegacyPass(CompileKernel, Recover); + return new HWAddressSanitizerLegacyPass(CompileKernel, Recover, + DisableOptimization); } -HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover) - : CompileKernel(CompileKernel), Recover(Recover) {} +HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover, + bool DisableOptimization) + : CompileKernel(CompileKernel), Recover(Recover), + DisableOptimization(DisableOptimization) {} PreservedAnalyses HWAddressSanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) { - HWAddressSanitizer HWASan(M, CompileKernel, Recover); + const StackSafetyGlobalInfo *SSI = nullptr; + if (shouldUseStackSafetyAnalysis(llvm::Triple(M.getTargetTriple()), + DisableOptimization)) + SSI = &MAM.getResult(M); + HWAddressSanitizer HWASan(M, CompileKernel, Recover, SSI); bool Modified = false; for (Function &F : M) Modified |= HWASan.sanitizeFunction(F); @@ -1245,7 +1294,9 @@ // dynamic alloca instrumentation for them as well. !AI.isUsedWithInAlloca() && // swifterror allocas are register promoted by ISel - !AI.isSwiftError()); + !AI.isSwiftError()) && + // safe allocas are not interesting + !(SSI && SSI->isSafe(AI)); } bool HWAddressSanitizer::sanitizeFunction(Function &F) { diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll b/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll @@ -0,0 +1,42 @@ +; RUN: opt -hwasan -hwasan-use-stack-safety=1 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=SAFETY +; RUN: opt -hwasan -hwasan-use-stack-safety=0 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=NOSAFETY + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +; Check a safe alloca to ensure it does not get a tag. +define i32 @test_load(i32* %a) sanitize_hwaddress { +entry: + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + %buf.sroa.0 = alloca i8, align 4 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0) + store volatile i8 0, i8* %buf.sroa.0, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0) + ret i32 0 +} + +; Check a non-safe alloca to ensure it gets a tag. +define i32 @test_use(i32* %a) sanitize_hwaddress { +entry: + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_generate_tag + %buf.sroa.0 = alloca i8, align 4 + call void @use(i8* nonnull %buf.sroa.0) + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0) + store volatile i8 0, i8* %buf.sroa.0, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0) + ret i32 0 +} + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) + +declare void @use(i8* nocapture) + +!8 = !{!9, !9, i64 0} +!9 = !{!"omnipotent char", !10, i64 0} +!10 = !{!"Simple C/C++ TBAA"}