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,17 @@ 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())); } static void addKernelHWAddressSanitizerPasses(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { + const PassManagerBuilderWrapper &BuilderWrapper = + static_cast(Builder); PM.add(createHWAddressSanitizerLegacyPassPass( - /*CompileKernel*/ true, /*Recover*/ true)); + /*CompileKernel*/ true, /*Recover*/ true, + BuilderWrapper.getTargetTriple())); } static void addGeneralOptsForMemorySanitizer(const PassManagerBuilder &Builder, @@ -1164,7 +1167,8 @@ 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)); } }; 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,11 @@ +// 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 + +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,11 @@ +// 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 + +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,20 @@ class HWAddressSanitizerPass : public PassInfoMixin { public: explicit HWAddressSanitizerPass(bool CompileKernel = false, - bool Recover = false); + bool Recover = false, + Triple TargetTriple = {}); PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); static bool isRequired() { return true; } private: bool CompileKernel; bool Recover; + Triple TargetTriple; }; FunctionPass *createHWAddressSanitizerLegacyPassPass(bool CompileKernel = false, - bool Recover = false); + bool Recover = false, + Triple TargetTriple = {}); 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,10 @@ 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")); + static cl::opt ClUARRetagToZero( "hwasan-uar-retag-to-zero", cl::desc("Clear alloca tags before returning from the function to allow " @@ -192,13 +197,31 @@ namespace { +bool shouldUsePageAliases(const Triple &TargetTriple) { + return ClUsePageAliases && TargetTriple.getArch() == Triple::x86_64; +// No one should use the option directly. +#pragma GCC poison ClUsePageAliases +} + +bool shouldInstrumentStack(const Triple &TargetTriple) { + return shouldUsePageAliases(TargetTriple) ? false : ClInstrumentStack; +// No one should use the option directly. +#pragma GCC poison ClInstrumentStack +} + +bool shouldUseStackSafetyAnalysis(const Triple &TargetTriple) { + return shouldInstrumentStack(TargetTriple) && ClUseStackSafety; +// 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 @@ -207,6 +230,8 @@ initializeModule(); } + void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; } + bool sanitizeFunction(Function &F); void initializeModule(); void createHwasanCtorComdat(); @@ -259,6 +284,7 @@ private: LLVMContext *C; Module &M; + const StackSafetyGlobalInfo *SSI; Triple TargetTriple; FunctionCallee HWAsanMemmove, HWAsanMemcpy, HWAsanMemset; FunctionCallee HWAsanHandleVfork; @@ -329,8 +355,10 @@ static char ID; explicit HWAddressSanitizerLegacyPass(bool CompileKernel = false, - bool Recover = false) - : FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover) { + bool Recover = false, + Triple TargetTriple = {}) + : FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover), + TargetTriple(TargetTriple) { initializeHWAddressSanitizerLegacyPassPass( *PassRegistry::getPassRegistry()); } @@ -338,11 +366,19 @@ StringRef getPassName() const override { return "HWAddressSanitizer"; } bool doInitialization(Module &M) override { - HWASan = std::make_unique(M, CompileKernel, Recover); + const StackSafetyGlobalInfo *SSI = nullptr; + HWASan = + std::make_unique(M, CompileKernel, Recover, SSI); return true; } bool runOnFunction(Function &F) override { + if (shouldUseStackSafetyAnalysis(TargetTriple)) { + // 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); } @@ -351,10 +387,18 @@ return false; } + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + if (shouldUseStackSafetyAnalysis(TargetTriple)) { + AU.addRequired(); + } + } + private: std::unique_ptr HWASan; bool CompileKernel; bool Recover; + Triple TargetTriple; }; } // end anonymous namespace @@ -370,18 +414,25 @@ "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) -FunctionPass *llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, - bool Recover) { +FunctionPass * +llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, bool Recover, + Triple TargetTriple) { assert(!CompileKernel || Recover); - return new HWAddressSanitizerLegacyPass(CompileKernel, Recover); + return new HWAddressSanitizerLegacyPass(CompileKernel, Recover, TargetTriple); } -HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover) - : CompileKernel(CompileKernel), Recover(Recover) {} +HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover, + Triple TargetTriple) + : CompileKernel(CompileKernel), Recover(Recover), + TargetTriple(TargetTriple) {} PreservedAnalyses HWAddressSanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) { - HWAddressSanitizer HWASan(M, CompileKernel, Recover); + const StackSafetyGlobalInfo *SSI = nullptr; + if (shouldUseStackSafetyAnalysis(TargetTriple)) { + SSI = &MAM.getResult(M); + } + HWAddressSanitizer HWASan(M, CompileKernel, Recover, SSI); bool Modified = false; for (Function &F : M) Modified |= HWASan.sanitizeFunction(F); @@ -503,9 +554,9 @@ // - Intel LAM (default) // - pointer aliasing (heap only) bool IsX86_64 = TargetTriple.getArch() == Triple::x86_64; - UsePageAliases = ClUsePageAliases && IsX86_64; + UsePageAliases = shouldUsePageAliases(TargetTriple); InstrumentWithCalls = IsX86_64 ? true : ClInstrumentWithCalls; - InstrumentStack = UsePageAliases ? false : ClInstrumentStack; + InstrumentStack = shouldInstrumentStack(TargetTriple); PointerTagShift = IsX86_64 ? 57 : 56; TagMaskByte = IsX86_64 ? 0x3F : 0xFF; @@ -1223,6 +1274,7 @@ } bool HWAddressSanitizer::isInterestingAlloca(const AllocaInst &AI) { + // clang-format off return (AI.getAllocatedType()->isSized() && // FIXME: instrument dynamic allocas, too AI.isStaticAlloca() && @@ -1235,7 +1287,10 @@ // 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)); + // clang-format on } bool HWAddressSanitizer::sanitizeFunction(Function &F) {