Skip to content

Commit

Permalink
[NewPM] Port HWASan and Kernel HWASan
Browse files Browse the repository at this point in the history
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
  • Loading branch information
PiJoules authored and MrSidims committed May 17, 2019
1 parent 0b129e6 commit d63cb49
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 53 deletions.
34 changes: 32 additions & 2 deletions clang/lib/CodeGen/BackendUtil.cpp
Expand Up @@ -59,6 +59,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"
Expand Down Expand Up @@ -273,12 +274,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));
}

Expand Down Expand Up @@ -979,6 +981,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.
Expand Down Expand Up @@ -1162,6 +1175,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));
Expand Down
34 changes: 34 additions & 0 deletions clang/test/CodeGen/hwasan-new-pm.c
@@ -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
2 changes: 1 addition & 1 deletion llvm/include/llvm/InitializePasses.h
Expand Up @@ -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&);
Expand Down
3 changes: 0 additions & 3 deletions llvm/include/llvm/Transforms/Instrumentation.h
Expand Up @@ -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>(),
Expand Down
41 changes: 41 additions & 0 deletions 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<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
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Passes/PassRegistry.def
Expand Up @@ -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())
Expand Down
133 changes: 87 additions & 46 deletions llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}

StringRef getPassName() const override { return "HWAddressSanitizer"; }
initializeModule(M);
}

bool runOnFunction(Function &F) override;
bool doInitialization(Module &M) override;
bool sanitizeFunction(Function &F);
void initializeModule(Module &M);

void initializeCallbacks(Module &M);

Expand Down Expand Up @@ -279,29 +277,61 @@ class HWAddressSanitizer : public FunctionPass {
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();

Expand All @@ -320,43 +350,54 @@ 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.
//
// 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) {
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Transforms/Instrumentation/Instrumentation.cpp
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions llvm/test/Instrumentation/HWAddressSanitizer/basic.ll
Expand Up @@ -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)

Expand Down

0 comments on commit d63cb49

Please sign in to comment.