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 @@ -57,6 +57,7 @@ #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Transforms/Instrumentation/BoundsChecking.h" #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" +#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" @@ -265,12 +266,13 @@ static_cast(Builder); const CodeGenOptions &CGOpts = BuilderWrapper.getCGOpts(); bool Recover = CGOpts.SanitizeRecover.has(SanitizerKind::HWAddress); - PM.add(createHWAddressSanitizerPass(/*CompileKernel*/ false, Recover)); + PM.add( + createHWAddressSanitizerLegacyPassPass(/*CompileKernel*/ false, Recover)); } static void addKernelHWAddressSanitizerPasses(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { - PM.add(createHWAddressSanitizerPass( + PM.add(createHWAddressSanitizerLegacyPassPass( /*CompileKernel*/ true, /*Recover*/ true)); } @@ -950,6 +952,12 @@ if (LangOpts.Sanitize.has(SanitizerKind::Thread)) { MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); } + + if (LangOpts.Sanitize.has(SanitizerKind::HWAddress)) { + bool Recover = CodeGenOpts.SanitizeRecover.has(SanitizerKind::HWAddress); + MPM.addPass(createModuleToFunctionPassAdaptor( + HWAddressSanitizerPass(/*CompileKernel=*/false, Recover))); + } } /// A clean version of `EmitAssembly` that uses the new pass manager. @@ -1133,6 +1141,16 @@ UseOdrIndicator)); }); } + if (LangOpts.Sanitize.has(SanitizerKind::HWAddress)) { + bool Recover = + CodeGenOpts.SanitizeRecover.has(SanitizerKind::HWAddress); + PB.registerOptimizerLastEPCallback( + [Recover](FunctionPassManager &FPM, + PassBuilder::OptimizationLevel Level) { + FPM.addPass(HWAddressSanitizerPass( + /*CompileKernel=*/false, Recover)); + }); + } if (Optional Options = getGCOVOptions(CodeGenOpts)) PB.registerPipelineStartEPCallback([Options](ModulePassManager &MPM) { MPM.addPass(GCOVProfilerPass(*Options)); diff --git a/clang/test/CodeGen/hwasan-new-pm.c b/clang/test/CodeGen/hwasan-new-pm.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/hwasan-new-pm.c @@ -0,0 +1,15 @@ +// Test that HWASan runs with the new pass manager +// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=hwaddress %s | FileCheck %s +// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=hwaddress -flto %s | FileCheck %s +// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=hwaddress -flto=thin %s | FileCheck %s +// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=hwaddress %s | FileCheck %s +// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=hwaddress -flto %s | FileCheck %s +// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=hwaddress -flto=thin %s | FileCheck %s --check-prefixes=THIN-LTO + +int foo(int *a) { return *a; } + +// All the cases above mark the function with sanitize_hwaddress. +// Only the thinlto case doesn't result in a __hwasan_init declaration. +// CHECK: __hwasan_init +// CHECK: sanitize_hwaddress +// THIN-LTO: sanitize_hwaddress diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -163,7 +163,7 @@ void initializeGlobalsAAWrapperPassPass(PassRegistry&); void initializeGuardWideningLegacyPassPass(PassRegistry&); void initializeHotColdSplittingLegacyPassPass(PassRegistry&); -void initializeHWAddressSanitizerPass(PassRegistry&); +void initializeHWAddressSanitizerLegacyPassPass(PassRegistry &); void initializeIPCPPass(PassRegistry&); void initializeIPSCCPLegacyPassPass(PassRegistry&); void initializeIRCELegacyPassPass(PassRegistry&); diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -152,9 +152,6 @@ ModulePass *createInstrOrderFilePass(); -FunctionPass *createHWAddressSanitizerPass(bool CompileKernel = false, - bool Recover = false); - // Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation ModulePass *createDataFlowSanitizerPass( const std::vector &ABIListFiles = std::vector(), diff --git a/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Transforms/Instrumentation/HWAddressSanitizer.h @@ -0,0 +1,41 @@ +//===--------- Definition of the HWAddressSanitizer class -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Hardware AddressSanitizer class which is a port of the +// legacy HWAddressSanitizer pass to use the new PassManager infrastructure. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_HWADDRESSSANITIZERPASS_H +#define LLVM_TRANSFORMS_INSTRUMENTATION_HWADDRESSSANITIZERPASS_H + +#include "llvm/IR/Function.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +/// This is a public interface to the hardware address sanitizer pass for +/// instrumenting code to check for various memory errors at runtime, similar to +/// AddressSanitizer but based on partial hardware assistance. +class HWAddressSanitizerPass : public PassInfoMixin { +public: + explicit HWAddressSanitizerPass(bool CompileKernel = false, + bool Recover = false); + PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); + +private: + bool CompileKernel; + bool Recover; +}; + +FunctionPass *createHWAddressSanitizerLegacyPassPass(bool CompileKernel = false, + bool Recover = false); + +} // namespace llvm + +#endif diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -94,6 +94,7 @@ #include "llvm/Transforms/Instrumentation/CGProfile.h" #include "llvm/Transforms/Instrumentation/ControlHeightReduction.h" #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" +#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Instrumentation/InstrOrderFile.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -235,6 +235,7 @@ FUNCTION_PASS("view-cfg-only", CFGOnlyViewerPass()) FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass()) FUNCTION_PASS("asan", AddressSanitizerPass(false, false, false)) +FUNCTION_PASS("hwasan", HWAddressSanitizerPass(false, false)) FUNCTION_PASS("msan", MemorySanitizerPass({})) FUNCTION_PASS("tsan", ThreadSanitizerPass()) #undef FUNCTION_PASS 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 @@ -11,6 +11,7 @@ /// based on tagged addressing. //===----------------------------------------------------------------------===// +#include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -164,22 +165,19 @@ /// An instrumentation pass implementing detection of addressability bugs /// using tagged pointers. -class HWAddressSanitizer : public FunctionPass { +class HWAddressSanitizer { public: - // Pass identification, replacement for typeid. - static char ID; - - explicit HWAddressSanitizer(bool CompileKernel = false, bool Recover = false) - : FunctionPass(ID) { + explicit HWAddressSanitizer(Module &M, bool CompileKernel = false, + bool Recover = false) { this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover; this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0 ? ClEnableKhwasan : CompileKernel; - } - StringRef getPassName() const override { return "HWAddressSanitizer"; } + initializeWithModule(M); + } - bool runOnFunction(Function &F) override; - bool doInitialization(Module &M) override; + bool instrumentFunction(Function &F); + void initializeWithModule(Module &M); void initializeCallbacks(Module &M); @@ -279,29 +277,61 @@ GlobalValue *ThreadPtrGlobal = nullptr; }; +class HWAddressSanitizerLegacyPass : public FunctionPass { +public: + // Pass identification, replacement for typeid. + static char ID; + + explicit HWAddressSanitizerLegacyPass(bool CompileKernel = false, + bool Recover = false) + : FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover) {} + + StringRef getPassName() const override { return "HWAddressSanitizer"; } + + bool runOnFunction(Function &F) override { + HWAddressSanitizer HWASan(*F.getParent(), CompileKernel, Recover); + return HWASan.instrumentFunction(F); + } + +private: + bool CompileKernel; + bool Recover; +}; + } // end anonymous namespace -char HWAddressSanitizer::ID = 0; +char HWAddressSanitizerLegacyPass::ID = 0; INITIALIZE_PASS_BEGIN( - HWAddressSanitizer, "hwasan", + HWAddressSanitizerLegacyPass, "hwasan", "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) INITIALIZE_PASS_END( - HWAddressSanitizer, "hwasan", + HWAddressSanitizerLegacyPass, "hwasan", "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) -FunctionPass *llvm::createHWAddressSanitizerPass(bool CompileKernel, - bool Recover) { +FunctionPass *llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, + bool Recover) { assert(!CompileKernel || Recover); - return new HWAddressSanitizer(CompileKernel, Recover); + return new HWAddressSanitizerLegacyPass(CompileKernel, Recover); +} + +HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover) + : CompileKernel(CompileKernel), Recover(Recover) {} + +PreservedAnalyses HWAddressSanitizerPass::run(Function &F, + FunctionAnalysisManager &FAM) { + HWAddressSanitizer HWASan(*F.getParent(), CompileKernel, Recover); + if (HWASan.instrumentFunction(F)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); } /// Module-level initialization. /// /// inserts a call to __hwasan_init to the module's constructor list. -bool HWAddressSanitizer::doInitialization(Module &M) { +void HWAddressSanitizer::initializeWithModule(Module &M) { LLVM_DEBUG(dbgs() << "Init " << M.getName() << "\n"); auto &DL = M.getDataLayout(); @@ -320,13 +350,24 @@ HwasanCtorFunction = nullptr; if (!CompileKernel) { std::tie(HwasanCtorFunction, std::ignore) = - createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName, - kHwasanInitName, - /*InitArgTypes=*/{}, - /*InitArgs=*/{}); - Comdat *CtorComdat = M.getOrInsertComdat(kHwasanModuleCtorName); - HwasanCtorFunction->setComdat(CtorComdat); - appendToGlobalCtors(M, HwasanCtorFunction, 0, HwasanCtorFunction); + getOrCreateSanitizerCtorAndInitFunctions( + M, kHwasanModuleCtorName, kHwasanInitName, + /*InitArgTypes=*/{}, + /*InitArgs=*/{}, + // This callback is invoked when the functions are created the first + // time. Hook them into the global ctors list in that case: + [&](Function *Ctor, FunctionCallee) { + Comdat *CtorComdat = M.getOrInsertComdat(kHwasanModuleCtorName); + Ctor->setComdat(CtorComdat); + appendToGlobalCtors(M, Ctor, 0, Ctor); + + IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator()); + IRBCtor.CreateCall( + declareSanitizerInitFunction(M, "__hwasan_init_frames", + {Int8PtrTy, Int8PtrTy}), + {createFrameSectionBound(M, Int8Ty, getFrameSectionBeg()), + createFrameSectionBound(M, Int8Ty, getFrameSectionEnd())}); + }); // Create a zero-length global in __hwasan_frame so that the linker will // always create start and stop symbols. @@ -334,29 +375,29 @@ // N.B. If we ever start creating associated metadata in this pass this // global will need to be associated with the ctor. Type *Int8Arr0Ty = ArrayType::get(Int8Ty, 0); - auto GV = - new GlobalVariable(M, Int8Arr0Ty, /*isConstantGlobal*/ true, - GlobalVariable::PrivateLinkage, - Constant::getNullValue(Int8Arr0Ty), "__hwasan"); - GV->setSection(getFrameSection()); - GV->setComdat(CtorComdat); - appendToCompilerUsed(M, GV); - - IRBuilder<> IRBCtor(HwasanCtorFunction->getEntryBlock().getTerminator()); - IRBCtor.CreateCall( - declareSanitizerInitFunction(M, "__hwasan_init_frames", - {Int8PtrTy, Int8PtrTy}), - {createFrameSectionBound(M, Int8Ty, getFrameSectionBeg()), - createFrameSectionBound(M, Int8Ty, getFrameSectionEnd())}); + M.getOrInsertGlobal("__hwasan", Int8Arr0Ty, [&] { + auto *GV = new GlobalVariable( + M, Int8Arr0Ty, /*isConstantGlobal=*/true, GlobalValue::PrivateLinkage, + Constant::getNullValue(Int8Arr0Ty), "__hwasan"); + GV->setSection(getFrameSection()); + Comdat *CtorComdat = M.getOrInsertComdat(kHwasanModuleCtorName); + GV->setComdat(CtorComdat); + appendToCompilerUsed(M, GV); + return GV; + }); } - if (!TargetTriple.isAndroid()) - appendToCompilerUsed( - M, ThreadPtrGlobal = new GlobalVariable( - M, IntptrTy, false, GlobalVariable::ExternalLinkage, nullptr, - "__hwasan_tls", nullptr, GlobalVariable::InitialExecTLSModel)); - - return true; + if (!TargetTriple.isAndroid()) { + Constant *C = M.getOrInsertGlobal("__hwasan_tls", IntptrTy, [&] { + auto *GV = new GlobalVariable(M, IntptrTy, /*isConstantGlobal=*/false, + GlobalValue::ExternalLinkage, nullptr, + "__hwasan_tls", nullptr, + GlobalVariable::InitialExecTLSModel); + appendToCompilerUsed(M, GV); + return GV; + }); + ThreadPtrGlobal = cast(C); + } } void HWAddressSanitizer::initializeCallbacks(Module &M) { @@ -970,7 +1011,7 @@ !AI.isSwiftError()); } -bool HWAddressSanitizer::runOnFunction(Function &F) { +bool HWAddressSanitizer::instrumentFunction(Function &F) { if (&F == HwasanCtorFunction) return false; diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp b/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp --- a/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -114,7 +114,7 @@ initializeInstrOrderFileLegacyPassPass(Registry); initializeInstrProfilingLegacyPassPass(Registry); initializeMemorySanitizerLegacyPassPass(Registry); - initializeHWAddressSanitizerPass(Registry); + initializeHWAddressSanitizerLegacyPassPass(Registry); initializeThreadSanitizerLegacyPassPass(Registry); initializeSanitizerCoverageModulePass(Registry); initializeDataFlowSanitizerPass(Registry); diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll --- a/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll @@ -5,6 +5,12 @@ ; RUN: opt < %s -hwasan -hwasan-recover=0 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,ABORT,ABORT-ZERO-BASED-SHADOW ; RUN: opt < %s -hwasan -hwasan-recover=1 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,RECOVER,RECOVER-ZERO-BASED-SHADOW +; Ensure than hwasan runs with the new PM pass +; RUN: opt < %s -passes='function(hwasan)' -hwasan-recover=0 -hwasan-with-ifunc=1 -hwasan-with-tls=0 -S | FileCheck %s --check-prefixes=CHECK,ABORT,ABORT-DYNAMIC-SHADOW +; RUN: opt < %s -passes='function(hwasan)' -hwasan-recover=1 -hwasan-with-ifunc=1 -hwasan-with-tls=0 -S | FileCheck %s --check-prefixes=CHECK,RECOVER,RECOVER-DYNAMIC-SHADOW +; RUN: opt < %s -passes='function(hwasan)' -hwasan-recover=0 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,ABORT,ABORT-ZERO-BASED-SHADOW +; RUN: opt < %s -passes='function(hwasan)' -hwasan-recover=1 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,RECOVER,RECOVER-ZERO-BASED-SHADOW + ; CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @hwasan.module_ctor, i8* bitcast (void ()* @hwasan.module_ctor to i8*) }] ; CHECK: @__hwasan = private constant [0 x i8] zeroinitializer, section "__hwasan_frames", comdat($hwasan.module_ctor)