[NewPM] Port HWASan and Kernel HWASan

Port hardware assisted address sanitizer to new PM following the same guidelines as msan and tsan.

Changes:
- Separate HWAddressSanitizer into a pass class and a sanitizer class.
- Create new PM wrapper pass for the sanitizer class.
- Use the getOrINsert pattern for some module level initialization declarations.
- Also enable kernel-kwasan in new PM
- Update llvm tests and add clang test.

Differential Revision: https://reviews.llvm.org/D61709

llvm-svn: 360707
This commit is contained in:
Leonard Chan 2019-05-14 21:17:21 +00:00
parent 7baf528aba
commit 0cdd3b1d81
10 changed files with 205 additions and 53 deletions

View File

@ -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 void addHWAddressSanitizerPasses(const PassManagerBuilder &Builder,
static_cast<const PassManagerBuilderWrapper &>(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));
}
@ -962,6 +964,17 @@ static void addSanitizersAtO0(ModulePassManager &MPM,
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)));
}
if (LangOpts.Sanitize.has(SanitizerKind::KernelHWAddress)) {
MPM.addPass(createModuleToFunctionPassAdaptor(
HWAddressSanitizerPass(/*CompileKernel=*/true, /*Recover=*/true)));
}
}
/// A clean version of `EmitAssembly` that uses the new pass manager.
@ -1145,6 +1158,23 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager(
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 (LangOpts.Sanitize.has(SanitizerKind::KernelHWAddress)) {
PB.registerOptimizerLastEPCallback(
[](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
FPM.addPass(HWAddressSanitizerPass(
/*CompileKernel=*/true, /*Recover=*/true));
});
}
if (Optional<GCOVOptions> Options = getGCOVOptions(CodeGenOpts))
PB.registerPipelineStartEPCallback([Options](ModulePassManager &MPM) {
MPM.addPass(GCOVProfilerPass(*Options));

View File

@ -0,0 +1,34 @@
// Test that HWASan and KHWASan runs with the new pass manager.
// We run them under different optimizations and LTOs to ensure the IR is still
// being instrumented properly.
// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=hwaddress %s | FileCheck %s --check-prefixes=CHECK,HWASAN,HWASAN-NOOPT
// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=hwaddress -flto %s | FileCheck %s --check-prefixes=CHECK,HWASAN,HWASAN-NOOPT
// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=hwaddress -flto=thin %s | FileCheck %s --check-prefixes=CHECK,HWASAN,HWASAN-NOOPT
// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=hwaddress %s | FileCheck %s --check-prefixes=CHECK,HWASAN
// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=hwaddress -flto %s | FileCheck %s --check-prefixes=CHECK,HWASAN
// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=hwaddress -flto=thin %s | FileCheck %s
// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=kernel-hwaddress %s | FileCheck %s --check-prefixes=CHECK,KHWASAN,KHWASAN-NOOPT
// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=kernel-hwaddress -flto %s | FileCheck %s --check-prefixes=CHECK,KHWASAN,KHWASAN-NOOPT
// RUN: %clang_cc1 -S -emit-llvm -o - -fexperimental-new-pass-manager -fsanitize=kernel-hwaddress -flto=thin %s | FileCheck %s --check-prefixes=CHECK,KHWASAN,KHWASAN-NOOPT
// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=kernel-hwaddress %s | FileCheck %s --check-prefixes=CHECK,KHWASAN
// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=kernel-hwaddress -flto %s | FileCheck %s --check-prefixes=CHECK,KHWASAN
// RUN: %clang_cc1 -S -emit-llvm -o - -O1 -fexperimental-new-pass-manager -fsanitize=kernel-hwaddress -flto=thin %s | FileCheck %s
int foo(int *a) { return *a; }
// All the cases above mark the function with sanitize_hwaddress.
// CHECK-DAG: sanitize_hwaddress
// Both sanitizers produce %hwasan.shadow without both thinlto and optimizations.
// HWASAN-DAG: %hwasan.shadow
// KHWASAN-DAG: %hwasan.shadow
// Both sanitizers produce __hwasan_tls without both thinlto and optimizations.
// HWASAN-DAG: __hwasan_tls
// KHWASAN-DAG: __hwasan_tls
// For unoptimized cases, both sanitizers produce different load functions.
// HWASAN-NOOPT-DAG: __hwasan_loadN
// KHWASAN-NOOPT-DAG: __hwasan_loadN_noabort

View File

@ -163,7 +163,7 @@ void initializeGlobalSplitPass(PassRegistry&);
void initializeGlobalsAAWrapperPassPass(PassRegistry&);
void initializeGuardWideningLegacyPassPass(PassRegistry&);
void initializeHotColdSplittingLegacyPassPass(PassRegistry&);
void initializeHWAddressSanitizerPass(PassRegistry&);
void initializeHWAddressSanitizerLegacyPassPass(PassRegistry &);
void initializeIPCPPass(PassRegistry&);
void initializeIPSCCPLegacyPassPass(PassRegistry&);
void initializeIRCELegacyPassPass(PassRegistry&);

View File

@ -152,9 +152,6 @@ ModulePass *createInstrProfilingLegacyPass(
ModulePass *createInstrOrderFilePass();
FunctionPass *createHWAddressSanitizerPass(bool CompileKernel = false,
bool Recover = false);
// Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation
ModulePass *createDataFlowSanitizerPass(
const std::vector<std::string> &ABIListFiles = std::vector<std::string>(),

View File

@ -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<HWAddressSanitizerPass> {
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

View File

@ -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"

View File

@ -237,6 +237,8 @@ FUNCTION_PASS("view-cfg-only", CFGOnlyViewerPass())
FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
FUNCTION_PASS("asan", AddressSanitizerPass(false, false, false))
FUNCTION_PASS("kasan", AddressSanitizerPass(true, false, false))
FUNCTION_PASS("hwasan", HWAddressSanitizerPass(false, false))
FUNCTION_PASS("khwasan", HWAddressSanitizerPass(true, true))
FUNCTION_PASS("msan", MemorySanitizerPass({}))
FUNCTION_PASS("kmsan", MemorySanitizerPass({0, false, /*Kernel=*/true}))
FUNCTION_PASS("tsan", ThreadSanitizerPass())

View File

@ -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 @@ namespace {
/// 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;
initializeModule(M);
}
StringRef getPassName() const override { return "HWAddressSanitizer"; }
bool runOnFunction(Function &F) override;
bool doInitialization(Module &M) override;
bool sanitizeFunction(Function &F);
void initializeModule(Module &M);
void initializeCallbacks(Module &M);
@ -279,29 +277,61 @@ private:
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.sanitizeFunction(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.sanitizeFunction(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::initializeModule(Module &M) {
LLVM_DEBUG(dbgs() << "Init " << M.getName() << "\n");
auto &DL = M.getDataLayout();
@ -320,13 +350,24 @@ bool HWAddressSanitizer::doInitialization(Module &M) {
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 @@ bool HWAddressSanitizer::doInitialization(Module &M) {
// 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<GlobalVariable>(C);
}
}
void HWAddressSanitizer::initializeCallbacks(Module &M) {
@ -970,7 +1011,7 @@ bool HWAddressSanitizer::isInterestingAlloca(const AllocaInst &AI) {
!AI.isSwiftError());
}
bool HWAddressSanitizer::runOnFunction(Function &F) {
bool HWAddressSanitizer::sanitizeFunction(Function &F) {
if (&F == HwasanCtorFunction)
return false;

View File

@ -114,7 +114,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) {
initializeInstrOrderFileLegacyPassPass(Registry);
initializeInstrProfilingLegacyPassPass(Registry);
initializeMemorySanitizerLegacyPassPass(Registry);
initializeHWAddressSanitizerPass(Registry);
initializeHWAddressSanitizerLegacyPassPass(Registry);
initializeThreadSanitizerLegacyPassPass(Registry);
initializeSanitizerCoverageModulePass(Registry);
initializeDataFlowSanitizerPass(Registry);

View File

@ -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)