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,20 @@ 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, BuilderWrapper.getTargetTriple(), + /*IsOptNull=*/CGOpts.OptimizationLevel == 0)); } static void addKernelHWAddressSanitizerPasses(const PassManagerBuilder &Builder, 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, + BuilderWrapper.getTargetTriple(), + /*IsOptNull=*/CGOpts.OptimizationLevel == 0)); } static void addGeneralOptsForMemorySanitizer(const PassManagerBuilder &Builder, @@ -1164,7 +1170,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, TargetTriple, + /*IsOptNull=*/CodeGenOpts.OptimizationLevel == 0)); } }; HWASanPass(SanitizerKind::HWAddress, false); diff --git a/clang/test/CodeGen/hwasan-stack-safety-analysis-asm.c b/clang/test/CodeGen/hwasan-stack-safety-analysis-asm.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/hwasan-stack-safety-analysis-asm.c @@ -0,0 +1,15 @@ +// RUN: %clang -fsanitize=hwaddress -target aarch64-linux-gnu -S -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 -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 -mllvm -hwasan-generate-tags-with-calls -O2 %s -o - | FileCheck %s --check-prefix=SAFETY +// RUN: %clang -fsanitize=hwaddress -target aarch64-linux-gnu -S -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/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 @@ -14,6 +14,8 @@ #ifndef LLVM_TRANSFORMS_INSTRUMENTATION_HWADDRESSSANITIZER_H #define LLVM_TRANSFORMS_INSTRUMENTATION_HWADDRESSSANITIZER_H +#include "llvm/ADT/Triple.h" + #include "llvm/IR/Function.h" #include "llvm/IR/PassManager.h" @@ -25,17 +27,23 @@ class HWAddressSanitizerPass : public PassInfoMixin { public: explicit HWAddressSanitizerPass(bool CompileKernel = false, - bool Recover = false); + bool Recover = false, + Triple TargetTriple = {}, + bool IsOptNull = false); PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); static bool isRequired() { return true; } private: bool CompileKernel; bool Recover; + Triple TargetTriple; + bool IsOptNull; }; FunctionPass *createHWAddressSanitizerLegacyPassPass(bool CompileKernel = false, - bool Recover = false); + bool Recover = false, + Triple TargetTriple = {}, + bool IsOptNull = 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 " @@ -210,13 +216,21 @@ #pragma GCC poison ClInstrumentWithCalls } +bool shouldUseStackSafetyAnalysis(const Triple &TargetTriple, bool IsOptNull) { + auto StackSafety = + ClUseStackSafety.getNumOccurrences() ? ClUseStackSafety : !IsOptNull; + return shouldInstrumentStack(TargetTriple) && StackSafety; +// No one should use the option directly. +#pragma GCC poison ClUseStackSafety +} /// An instrumentation pass implementing detection of addressability bugs /// using tagged pointers. class HWAddressSanitizer { public: explicit HWAddressSanitizer(Module &M, bool CompileKernel = false, - bool Recover = false) - : M(M) { + bool Recover = false, + const StackSafetyGlobalInfo *SSI = nullptr) + : M(M), SSI(SSI) { this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0 ? ClEnableKhwasan @@ -225,6 +239,8 @@ initializeModule(); } + void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; } + bool sanitizeFunction(Function &F); void initializeModule(); void createHwasanCtorComdat(); @@ -277,6 +293,7 @@ private: LLVMContext *C; Module &M; + const StackSafetyGlobalInfo *SSI; Triple TargetTriple; FunctionCallee HWAsanMemmove, HWAsanMemcpy, HWAsanMemset; FunctionCallee HWAsanHandleVfork; @@ -347,8 +364,11 @@ static char ID; explicit HWAddressSanitizerLegacyPass(bool CompileKernel = false, - bool Recover = false) - : FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover) { + bool Recover = false, + Triple TargetTriple = {}, + bool IsOptNull = false) + : FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover), + TargetTriple(TargetTriple) { initializeHWAddressSanitizerLegacyPassPass( *PassRegistry::getPassRegistry()); } @@ -356,11 +376,18 @@ 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(TargetTriple, IsOptNull)) { + // 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); } @@ -369,10 +396,18 @@ return false; } + void getAnalysisUsage(AnalysisUsage &AU) const override { + if (shouldUseStackSafetyAnalysis(TargetTriple, IsOptNull)) { + AU.addRequired(); + } + } + private: std::unique_ptr HWASan; bool CompileKernel; bool Recover; + Triple TargetTriple; + bool IsOptNull; }; } // end anonymous namespace @@ -389,17 +424,27 @@ false) FunctionPass *llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, - bool Recover) { + bool Recover, + Triple TargetTriple, + bool IsOptNull) { assert(!CompileKernel || Recover); - return new HWAddressSanitizerLegacyPass(CompileKernel, Recover); + return new HWAddressSanitizerLegacyPass(CompileKernel, Recover, TargetTriple, + IsOptNull); } -HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover) - : CompileKernel(CompileKernel), Recover(Recover) {} +HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover, + Triple TargetTriple, + bool IsOptNull) + : CompileKernel(CompileKernel), Recover(Recover), + TargetTriple(TargetTriple), IsOptNull(IsOptNull) {} PreservedAnalyses HWAddressSanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) { - HWAddressSanitizer HWASan(M, CompileKernel, Recover); + const StackSafetyGlobalInfo *SSI = nullptr; + if (shouldUseStackSafetyAnalysis(TargetTriple, IsOptNull)) { + SSI = &MAM.getResult(M); + } + HWAddressSanitizer HWASan(M, CompileKernel, Recover, SSI); bool Modified = false; for (Function &F : M) Modified |= HWASan.sanitizeFunction(F); @@ -1253,7 +1298,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) {