1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 12:41:49 +01:00

Hardware-assisted AddressSanitizer (llvm part).

Summary:
This is LLVM instrumentation for the new HWASan tool. It is basically
a stripped down copy of ASan at this point, w/o stack or global
support. Instrumenation adds a global constructor + runtime callbacks
for every load and store.

HWASan comes with its own IR attribute.

A brief design document can be found in
clang/docs/HardwareAssistedAddressSanitizerDesign.rst (submitted earlier).

Reviewers: kcc, pcc, alekseyshl

Subscribers: srhines, mehdi_amini, mgorny, javed.absar, eraman, llvm-commits, hiraditya

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

llvm-svn: 320217
This commit is contained in:
Evgeniy Stepanov 2017-12-09 00:21:41 +00:00
parent 716568d4d2
commit 67168a732b
30 changed files with 676 additions and 17 deletions

View File

@ -1054,6 +1054,7 @@ The integer codes are mapped to well-known attributes as follows.
* code 52: ``writeonly`` * code 52: ``writeonly``
* code 53: ``speculatable`` * code 53: ``speculatable``
* code 54: ``strictfp`` * code 54: ``strictfp``
* code 55: ``sanitize_hwaddress``
.. note:: .. note::
The ``allocsize`` attribute has a special encoding for its arguments. Its two The ``allocsize`` attribute has a special encoding for its arguments. Its two

View File

@ -1597,6 +1597,10 @@ example:
``sanitize_thread`` ``sanitize_thread``
This attribute indicates that ThreadSanitizer checks This attribute indicates that ThreadSanitizer checks
(dynamic thread safety analysis) are enabled for this function. (dynamic thread safety analysis) are enabled for this function.
``sanitize_hwaddress``
This attribute indicates that HWAddressSanitizer checks
(dynamic address safety analysis based on tagged pointers) are enabled for
this function.
``speculatable`` ``speculatable``
This function attribute indicates that the function does not have any This function attribute indicates that the function does not have any
effects besides calculating its result and does not have undefined behavior. effects besides calculating its result and does not have undefined behavior.

View File

@ -560,6 +560,7 @@ enum AttributeKindCodes {
ATTR_KIND_WRITEONLY = 52, ATTR_KIND_WRITEONLY = 52,
ATTR_KIND_SPECULATABLE = 53, ATTR_KIND_SPECULATABLE = 53,
ATTR_KIND_STRICT_FP = 54, ATTR_KIND_STRICT_FP = 54,
ATTR_KIND_SANITIZE_HWADDRESS = 55,
}; };
enum ComdatSelectionKindCodes { enum ComdatSelectionKindCodes {

View File

@ -164,6 +164,9 @@ def SanitizeThread : EnumAttr<"sanitize_thread">;
/// MemorySanitizer is on. /// MemorySanitizer is on.
def SanitizeMemory : EnumAttr<"sanitize_memory">; def SanitizeMemory : EnumAttr<"sanitize_memory">;
/// HWAddressSanitizer is on.
def SanitizeHWAddress : EnumAttr<"sanitize_hwaddress">;
/// Argument is swift error. /// Argument is swift error.
def SwiftError : EnumAttr<"swifterror">; def SwiftError : EnumAttr<"swifterror">;
@ -200,6 +203,7 @@ class CompatRule<string F> {
def : CompatRule<"isEqual<SanitizeAddressAttr>">; def : CompatRule<"isEqual<SanitizeAddressAttr>">;
def : CompatRule<"isEqual<SanitizeThreadAttr>">; def : CompatRule<"isEqual<SanitizeThreadAttr>">;
def : CompatRule<"isEqual<SanitizeMemoryAttr>">; def : CompatRule<"isEqual<SanitizeMemoryAttr>">;
def : CompatRule<"isEqual<SanitizeHWAddressAttr>">;
def : CompatRule<"isEqual<SafeStackAttr>">; def : CompatRule<"isEqual<SafeStackAttr>">;
class MergeRule<string F> { class MergeRule<string F> {

View File

@ -360,6 +360,7 @@ void initializeStripNonDebugSymbolsPass(PassRegistry&);
void initializeStripNonLineTableDebugInfoPass(PassRegistry&); void initializeStripNonLineTableDebugInfoPass(PassRegistry&);
void initializeStripSymbolsPass(PassRegistry&); void initializeStripSymbolsPass(PassRegistry&);
void initializeStructurizeCFGPass(PassRegistry&); void initializeStructurizeCFGPass(PassRegistry&);
void initializeHWAddressSanitizerPass(PassRegistry&);
void initializeTailCallElimPass(PassRegistry&); void initializeTailCallElimPass(PassRegistry&);
void initializeTailDuplicatePassPass(PassRegistry&); void initializeTailDuplicatePassPass(PassRegistry&);
void initializeTargetLibraryInfoWrapperPassPass(PassRegistry&); void initializeTargetLibraryInfoWrapperPassPass(PassRegistry&);

View File

@ -133,6 +133,8 @@ ModulePass *createAddressSanitizerModulePass(bool CompileKernel = false,
FunctionPass *createMemorySanitizerPass(int TrackOrigins = 0, FunctionPass *createMemorySanitizerPass(int TrackOrigins = 0,
bool Recover = false); bool Recover = false);
FunctionPass *createHWAddressSanitizerPass();
// Insert ThreadSanitizer (race detection) instrumentation // Insert ThreadSanitizer (race detection) instrumentation
FunctionPass *createThreadSanitizerPass(); FunctionPass *createThreadSanitizerPass();

View File

@ -306,8 +306,10 @@ unsigned MemoryDependenceResults::getLoadLoadClobberFullWidthSize(
return 0; return 0;
if (LIOffs + NewLoadByteSize > MemLocEnd && if (LIOffs + NewLoadByteSize > MemLocEnd &&
LI->getParent()->getParent()->hasFnAttribute( (LI->getParent()->getParent()->hasFnAttribute(
Attribute::SanitizeAddress)) Attribute::SanitizeAddress) ||
LI->getParent()->getParent()->hasFnAttribute(
Attribute::SanitizeHWAddress)))
// We will be reading past the location accessed by the original program. // We will be reading past the location accessed by the original program.
// While this is safe in a regular build, Address Safety analysis tools // While this is safe in a regular build, Address Safety analysis tools
// may start reporting false warnings. So, don't do widening. // may start reporting false warnings. So, don't do widening.

View File

@ -3511,7 +3511,8 @@ bool llvm::isSafeToSpeculativelyExecute(const Value *V,
// Speculative load may create a race that did not exist in the source. // Speculative load may create a race that did not exist in the source.
LI->getFunction()->hasFnAttribute(Attribute::SanitizeThread) || LI->getFunction()->hasFnAttribute(Attribute::SanitizeThread) ||
// Speculative load may load data from dirty regions. // Speculative load may load data from dirty regions.
LI->getFunction()->hasFnAttribute(Attribute::SanitizeAddress)) LI->getFunction()->hasFnAttribute(Attribute::SanitizeAddress) ||
LI->getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress))
return false; return false;
const DataLayout &DL = LI->getModule()->getDataLayout(); const DataLayout &DL = LI->getModule()->getDataLayout();
return isDereferenceableAndAlignedPointer(LI->getPointerOperand(), return isDereferenceableAndAlignedPointer(LI->getPointerOperand(),

View File

@ -664,6 +664,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(strictfp); KEYWORD(strictfp);
KEYWORD(safestack); KEYWORD(safestack);
KEYWORD(sanitize_address); KEYWORD(sanitize_address);
KEYWORD(sanitize_hwaddress);
KEYWORD(sanitize_thread); KEYWORD(sanitize_thread);
KEYWORD(sanitize_memory); KEYWORD(sanitize_memory);
KEYWORD(swifterror); KEYWORD(swifterror);

View File

@ -1144,6 +1144,8 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break; case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break;
case lltok::kw_sanitize_address: case lltok::kw_sanitize_address:
B.addAttribute(Attribute::SanitizeAddress); break; B.addAttribute(Attribute::SanitizeAddress); break;
case lltok::kw_sanitize_hwaddress:
B.addAttribute(Attribute::SanitizeHWAddress); break;
case lltok::kw_sanitize_thread: case lltok::kw_sanitize_thread:
B.addAttribute(Attribute::SanitizeThread); break; B.addAttribute(Attribute::SanitizeThread); break;
case lltok::kw_sanitize_memory: case lltok::kw_sanitize_memory:
@ -1468,6 +1470,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_optsize: case lltok::kw_optsize:
case lltok::kw_returns_twice: case lltok::kw_returns_twice:
case lltok::kw_sanitize_address: case lltok::kw_sanitize_address:
case lltok::kw_sanitize_hwaddress:
case lltok::kw_sanitize_memory: case lltok::kw_sanitize_memory:
case lltok::kw_sanitize_thread: case lltok::kw_sanitize_thread:
case lltok::kw_ssp: case lltok::kw_ssp:
@ -1560,6 +1563,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_optsize: case lltok::kw_optsize:
case lltok::kw_returns_twice: case lltok::kw_returns_twice:
case lltok::kw_sanitize_address: case lltok::kw_sanitize_address:
case lltok::kw_sanitize_hwaddress:
case lltok::kw_sanitize_memory: case lltok::kw_sanitize_memory:
case lltok::kw_sanitize_thread: case lltok::kw_sanitize_thread:
case lltok::kw_ssp: case lltok::kw_ssp:

View File

@ -172,6 +172,7 @@ enum Kind {
kw_alwaysinline, kw_alwaysinline,
kw_argmemonly, kw_argmemonly,
kw_sanitize_address, kw_sanitize_address,
kw_sanitize_hwaddress,
kw_builtin, kw_builtin,
kw_byval, kw_byval,
kw_inalloca, kw_inalloca,

View File

@ -1156,6 +1156,7 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
case Attribute::WriteOnly: return 1ULL << 53; case Attribute::WriteOnly: return 1ULL << 53;
case Attribute::Speculatable: return 1ULL << 54; case Attribute::Speculatable: return 1ULL << 54;
case Attribute::StrictFP: return 1ULL << 55; case Attribute::StrictFP: return 1ULL << 55;
case Attribute::SanitizeHWAddress: return 1ULL << 56;
case Attribute::Dereferenceable: case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format"); llvm_unreachable("dereferenceable attribute not supported in raw format");
break; break;
@ -1368,6 +1369,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::StructRet; return Attribute::StructRet;
case bitc::ATTR_KIND_SANITIZE_ADDRESS: case bitc::ATTR_KIND_SANITIZE_ADDRESS:
return Attribute::SanitizeAddress; return Attribute::SanitizeAddress;
case bitc::ATTR_KIND_SANITIZE_HWADDRESS:
return Attribute::SanitizeHWAddress;
case bitc::ATTR_KIND_SANITIZE_THREAD: case bitc::ATTR_KIND_SANITIZE_THREAD:
return Attribute::SanitizeThread; return Attribute::SanitizeThread;
case bitc::ATTR_KIND_SANITIZE_MEMORY: case bitc::ATTR_KIND_SANITIZE_MEMORY:

View File

@ -663,6 +663,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_STRUCT_RET; return bitc::ATTR_KIND_STRUCT_RET;
case Attribute::SanitizeAddress: case Attribute::SanitizeAddress:
return bitc::ATTR_KIND_SANITIZE_ADDRESS; return bitc::ATTR_KIND_SANITIZE_ADDRESS;
case Attribute::SanitizeHWAddress:
return bitc::ATTR_KIND_SANITIZE_HWADDRESS;
case Attribute::SanitizeThread: case Attribute::SanitizeThread:
return bitc::ATTR_KIND_SANITIZE_THREAD; return bitc::ATTR_KIND_SANITIZE_THREAD;
case Attribute::SanitizeMemory: case Attribute::SanitizeMemory:

View File

@ -558,16 +558,17 @@ bool ShrinkWrap::isShrinkWrapEnabled(const MachineFunction &MF) {
switch (EnableShrinkWrapOpt) { switch (EnableShrinkWrapOpt) {
case cl::BOU_UNSET: case cl::BOU_UNSET:
return TFI->enableShrinkWrapping(MF) && return TFI->enableShrinkWrapping(MF) &&
// Windows with CFI has some limitations that make it impossible // Windows with CFI has some limitations that make it impossible
// to use shrink-wrapping. // to use shrink-wrapping.
!MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && !MF.getTarget().getMCAsmInfo()->usesWindowsCFI() &&
// Sanitizers look at the value of the stack at the location // Sanitizers look at the value of the stack at the location
// of the crash. Since a crash can happen anywhere, the // of the crash. Since a crash can happen anywhere, the
// frame must be lowered before anything else happen for the // frame must be lowered before anything else happen for the
// sanitizers to be able to get a correct stack frame. // sanitizers to be able to get a correct stack frame.
!(MF.getFunction()->hasFnAttribute(Attribute::SanitizeAddress) || !(MF.getFunction()->hasFnAttribute(Attribute::SanitizeAddress) ||
MF.getFunction()->hasFnAttribute(Attribute::SanitizeThread) || MF.getFunction()->hasFnAttribute(Attribute::SanitizeThread) ||
MF.getFunction()->hasFnAttribute(Attribute::SanitizeMemory)); MF.getFunction()->hasFnAttribute(Attribute::SanitizeMemory) ||
MF.getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress));
// If EnableShrinkWrap is set, it takes precedence on whatever the // If EnableShrinkWrap is set, it takes precedence on whatever the
// target sets. The rational is that we assume we want to test // target sets. The rational is that we assume we want to test
// something related to shrink-wrapping. // something related to shrink-wrapping.

View File

@ -245,6 +245,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
if (hasAttribute(Attribute::SanitizeAddress)) if (hasAttribute(Attribute::SanitizeAddress))
return "sanitize_address"; return "sanitize_address";
if (hasAttribute(Attribute::SanitizeHWAddress))
return "sanitize_hwaddress";
if (hasAttribute(Attribute::AlwaysInline)) if (hasAttribute(Attribute::AlwaysInline))
return "alwaysinline"; return "alwaysinline";
if (hasAttribute(Attribute::ArgMemOnly)) if (hasAttribute(Attribute::ArgMemOnly))

View File

@ -1375,6 +1375,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
case Attribute::NonLazyBind: case Attribute::NonLazyBind:
case Attribute::ReturnsTwice: case Attribute::ReturnsTwice:
case Attribute::SanitizeAddress: case Attribute::SanitizeAddress:
case Attribute::SanitizeHWAddress:
case Attribute::SanitizeThread: case Attribute::SanitizeThread:
case Attribute::SanitizeMemory: case Attribute::SanitizeMemory:
case Attribute::MinSize: case Attribute::MinSize:

View File

@ -52,6 +52,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
.Case("returns_twice", Attribute::ReturnsTwice) .Case("returns_twice", Attribute::ReturnsTwice)
.Case("safestack", Attribute::SafeStack) .Case("safestack", Attribute::SafeStack)
.Case("sanitize_address", Attribute::SanitizeAddress) .Case("sanitize_address", Attribute::SanitizeAddress)
.Case("sanitize_hwaddress", Attribute::SanitizeHWAddress)
.Case("sanitize_memory", Attribute::SanitizeMemory) .Case("sanitize_memory", Attribute::SanitizeMemory)
.Case("sanitize_thread", Attribute::SanitizeThread) .Case("sanitize_thread", Attribute::SanitizeThread)
.Case("ssp", Attribute::StackProtect) .Case("ssp", Attribute::StackProtect)

View File

@ -3607,7 +3607,8 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
case Intrinsic::lifetime_start: case Intrinsic::lifetime_start:
// Asan needs to poison memory to detect invalid access which is possible // Asan needs to poison memory to detect invalid access which is possible
// even for empty lifetime range. // even for empty lifetime range.
if (II->getFunction()->hasFnAttribute(Attribute::SanitizeAddress)) if (II->getFunction()->hasFnAttribute(Attribute::SanitizeAddress) ||
II->getFunction()->hasFnAttribute(Attribute::SanitizeHWAddress))
break; break;
if (removeTriviallyEmptyRange(*II, Intrinsic::lifetime_start, if (removeTriviallyEmptyRange(*II, Intrinsic::lifetime_start,

View File

@ -12,6 +12,7 @@ add_llvm_library(LLVMInstrumentation
SanitizerCoverage.cpp SanitizerCoverage.cpp
ThreadSanitizer.cpp ThreadSanitizer.cpp
EfficiencySanitizer.cpp EfficiencySanitizer.cpp
HWAddressSanitizer.cpp
ADDITIONAL_HEADER_DIRS ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms

View File

@ -0,0 +1,282 @@
//===- HWAddressSanitizer.cpp - detector of uninitialized reads -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
/// \file
/// This file is a part of HWAddressSanitizer, an address sanity checker
/// based on tagged addressing.
//===----------------------------------------------------------------------===//
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
#define DEBUG_TYPE "hwasan"
static const char *const kHwasanModuleCtorName = "hwasan.module_ctor";
static const char *const kHwasanInitName = "__hwasan_init";
// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
static const size_t kNumberOfAccessSizes = 5;
static cl::opt<std::string> ClMemoryAccessCallbackPrefix(
"hwasan-memory-access-callback-prefix",
cl::desc("Prefix for memory access callbacks"), cl::Hidden,
cl::init("__hwasan_"));
static cl::opt<bool> ClInstrumentReads("hwasan-instrument-reads",
cl::desc("instrument read instructions"),
cl::Hidden, cl::init(true));
static cl::opt<bool> ClInstrumentWrites(
"hwasan-instrument-writes", cl::desc("instrument write instructions"),
cl::Hidden, cl::init(true));
static cl::opt<bool> ClInstrumentAtomics(
"hwasan-instrument-atomics",
cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
cl::init(true));
namespace {
/// \brief An instrumentation pass implementing detection of addressability bugs
/// using tagged pointers.
class HWAddressSanitizer : public FunctionPass {
public:
// Pass identification, replacement for typeid.
static char ID;
HWAddressSanitizer() : FunctionPass(ID) {}
StringRef getPassName() const override { return "HWAddressSanitizer"; }
bool runOnFunction(Function &F) override;
bool doInitialization(Module &M) override;
void initializeCallbacks(Module &M);
bool instrumentMemAccess(Instruction *I);
Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
uint64_t *TypeSize, unsigned *Alignment,
Value **MaybeMask);
private:
LLVMContext *C;
Type *IntptrTy;
Function *HwasanCtorFunction;
Function *HwasanMemoryAccessCallback[2][kNumberOfAccessSizes];
Function *HwasanMemoryAccessCallbackSized[2];
};
} // end anonymous namespace
char HWAddressSanitizer::ID = 0;
INITIALIZE_PASS_BEGIN(
HWAddressSanitizer, "hwasan",
"HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
INITIALIZE_PASS_END(
HWAddressSanitizer, "hwasan",
"HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
FunctionPass *llvm::createHWAddressSanitizerPass() {
return new HWAddressSanitizer();
}
/// \brief Module-level initialization.
///
/// inserts a call to __hwasan_init to the module's constructor list.
bool HWAddressSanitizer::doInitialization(Module &M) {
DEBUG(dbgs() << "Init " << M.getName() << "\n");
auto &DL = M.getDataLayout();
Triple TargetTriple(M.getTargetTriple());
C = &(M.getContext());
IRBuilder<> IRB(*C);
IntptrTy = IRB.getIntPtrTy(DL);
std::tie(HwasanCtorFunction, std::ignore) =
createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName,
kHwasanInitName,
/*InitArgTypes=*/{},
/*InitArgs=*/{});
appendToGlobalCtors(M, HwasanCtorFunction, 0);
return true;
}
void HWAddressSanitizer::initializeCallbacks(Module &M) {
IRBuilder<> IRB(*C);
for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) {
const std::string TypeStr = AccessIsWrite ? "store" : "load";
HwasanMemoryAccessCallbackSized[AccessIsWrite] =
checkSanitizerInterfaceFunction(M.getOrInsertFunction(
ClMemoryAccessCallbackPrefix + TypeStr,
FunctionType::get(IRB.getVoidTy(), {IntptrTy, IntptrTy}, false)));
for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
AccessSizeIndex++) {
HwasanMemoryAccessCallback[AccessIsWrite][AccessSizeIndex] =
checkSanitizerInterfaceFunction(M.getOrInsertFunction(
ClMemoryAccessCallbackPrefix + TypeStr +
itostr(1ULL << AccessSizeIndex),
FunctionType::get(IRB.getVoidTy(), {IntptrTy}, false)));
}
}
}
Value *HWAddressSanitizer::isInterestingMemoryAccess(Instruction *I,
bool *IsWrite,
uint64_t *TypeSize,
unsigned *Alignment,
Value **MaybeMask) {
// Skip memory accesses inserted by another instrumentation.
if (I->getMetadata("nosanitize")) return nullptr;
Value *PtrOperand = nullptr;
const DataLayout &DL = I->getModule()->getDataLayout();
if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
if (!ClInstrumentReads) return nullptr;
*IsWrite = false;
*TypeSize = DL.getTypeStoreSizeInBits(LI->getType());
*Alignment = LI->getAlignment();
PtrOperand = LI->getPointerOperand();
} else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
if (!ClInstrumentWrites) return nullptr;
*IsWrite = true;
*TypeSize = DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType());
*Alignment = SI->getAlignment();
PtrOperand = SI->getPointerOperand();
} else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
if (!ClInstrumentAtomics) return nullptr;
*IsWrite = true;
*TypeSize = DL.getTypeStoreSizeInBits(RMW->getValOperand()->getType());
*Alignment = 0;
PtrOperand = RMW->getPointerOperand();
} else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
if (!ClInstrumentAtomics) return nullptr;
*IsWrite = true;
*TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
*Alignment = 0;
PtrOperand = XCHG->getPointerOperand();
}
if (PtrOperand) {
// Do not instrument acesses from different address spaces; we cannot deal
// with them.
Type *PtrTy = cast<PointerType>(PtrOperand->getType()->getScalarType());
if (PtrTy->getPointerAddressSpace() != 0)
return nullptr;
// Ignore swifterror addresses.
// swifterror memory addresses are mem2reg promoted by instruction
// selection. As such they cannot have regular uses like an instrumentation
// function and it makes no sense to track them as memory.
if (PtrOperand->isSwiftError())
return nullptr;
}
return PtrOperand;
}
static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
size_t Res = countTrailingZeros(TypeSize / 8);
assert(Res < kNumberOfAccessSizes);
return Res;
}
bool HWAddressSanitizer::instrumentMemAccess(Instruction *I) {
DEBUG(dbgs() << "Instrumenting: " << *I << "\n");
bool IsWrite = false;
unsigned Alignment = 0;
uint64_t TypeSize = 0;
Value *MaybeMask = nullptr;
Value *Addr =
isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask);
if (!Addr)
return false;
if (MaybeMask)
return false; //FIXME
IRBuilder<> IRB(I);
Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
if (isPowerOf2_64(TypeSize) &&
(TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1)))) {
size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex],
AddrLong);
} else {
IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite],
{AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)});
}
return true;
}
bool HWAddressSanitizer::runOnFunction(Function &F) {
if (&F == HwasanCtorFunction)
return false;
if (!F.hasFnAttribute(Attribute::SanitizeHWAddress))
return false;
DEBUG(dbgs() << "Function: " << F.getName() << "\n");
initializeCallbacks(*F.getParent());
bool Changed = false;
SmallVector<Instruction*, 16> ToInstrument;
for (auto &BB : F) {
for (auto &Inst : BB) {
Value *MaybeMask = nullptr;
bool IsWrite;
unsigned Alignment;
uint64_t TypeSize;
Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize,
&Alignment, &MaybeMask);
if (Addr || isa<MemIntrinsic>(Inst))
ToInstrument.push_back(&Inst);
}
}
for (auto Inst : ToInstrument)
Changed |= instrumentMemAccess(Inst);
return Changed;
}

View File

@ -66,6 +66,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) {
initializePGOMemOPSizeOptLegacyPassPass(Registry); initializePGOMemOPSizeOptLegacyPassPass(Registry);
initializeInstrProfilingLegacyPassPass(Registry); initializeInstrProfilingLegacyPassPass(Registry);
initializeMemorySanitizerPass(Registry); initializeMemorySanitizerPass(Registry);
initializeHWAddressSanitizerPass(Registry);
initializeThreadSanitizerPass(Registry); initializeThreadSanitizerPass(Registry);
initializeSanitizerCoverageModulePass(Registry); initializeSanitizerCoverageModulePass(Registry);
initializeDataFlowSanitizerPass(Registry); initializeDataFlowSanitizerPass(Registry);

View File

@ -1299,7 +1299,10 @@ static void reportLoadElim(LoadInst *LI, Value *AvailableValue,
/// non-local by performing PHI construction. /// non-local by performing PHI construction.
bool GVN::processNonLocalLoad(LoadInst *LI) { bool GVN::processNonLocalLoad(LoadInst *LI) {
// non-local speculations are not allowed under asan. // non-local speculations are not allowed under asan.
if (LI->getParent()->getParent()->hasFnAttribute(Attribute::SanitizeAddress)) if (LI->getParent()->getParent()->hasFnAttribute(
Attribute::SanitizeAddress) ||
LI->getParent()->getParent()->hasFnAttribute(
Attribute::SanitizeHWAddress))
return false; return false;
// Step 1: Find the non-local dependencies of the load. // Step 1: Find the non-local dependencies of the load.

View File

@ -204,7 +204,7 @@ define void @f34()
; CHECK: define void @f34() ; CHECK: define void @f34()
{ {
call void @nobuiltin() nobuiltin call void @nobuiltin() nobuiltin
; CHECK: call void @nobuiltin() #34 ; CHECK: call void @nobuiltin() #35
ret void; ret void;
} }
@ -339,6 +339,12 @@ define void @f57() speculatable {
ret void ret void
} }
; CHECK: define void @f58() #34
define void @f58() sanitize_hwaddress
{
ret void;
}
; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone } ; CHECK: attributes #2 = { readnone }
@ -373,4 +379,5 @@ define void @f57() speculatable {
; CHECK: attributes #31 = { allocsize(0,1) } ; CHECK: attributes #31 = { allocsize(0,1) }
; CHECK: attributes #32 = { writeonly } ; CHECK: attributes #32 = { writeonly }
; CHECK: attributes #33 = { speculatable } ; CHECK: attributes #33 = { speculatable }
; CHECK: attributes #34 = { nobuiltin } ; CHECK: attributes #34 = { sanitize_hwaddress }
; CHECK: attributes #35 = { nobuiltin }

View File

@ -0,0 +1,26 @@
; Test basic address sanitizer instrumentation.
;
; RUN: opt < %s -hwasan -S | FileCheck %s
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-android"
define void @atomicrmw(i64* %ptr) sanitize_hwaddress {
; CHECK-LABEL: @atomicrmw(
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
; CHECK: call void @__hwasan_store8(i64 %[[A]])
entry:
%0 = atomicrmw add i64* %ptr, i64 1 seq_cst
ret void
}
define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddress {
; CHECK-LABEL: @cmpxchg(
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
; CHECK: call void @__hwasan_store8(i64 %[[A]])
entry:
%0 = cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst
ret void
}

View File

@ -0,0 +1,190 @@
; Test basic address sanitizer instrumentation.
;
; RUN: opt < %s -hwasan -S | FileCheck %s
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-android"
define i8 @test_load8(i8* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load8(
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: call void @__hwasan_load1(i64 %[[A]])
; CHECK: %[[B:[^ ]*]] = load i8, i8* %a
; CHECK: ret i8 %[[B]]
entry:
%b = load i8, i8* %a, align 4
ret i8 %b
}
define i16 @test_load16(i16* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load16(
; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64
; CHECK: call void @__hwasan_load2(i64 %[[A]])
; CHECK: %[[B:[^ ]*]] = load i16, i16* %a
; CHECK: ret i16 %[[B]]
entry:
%b = load i16, i16* %a, align 4
ret i16 %b
}
define i32 @test_load32(i32* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load32(
; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64
; CHECK: call void @__hwasan_load4(i64 %[[A]])
; CHECK: %[[B:[^ ]*]] = load i32, i32* %a
; CHECK: ret i32 %[[B]]
entry:
%b = load i32, i32* %a, align 4
ret i32 %b
}
define i64 @test_load64(i64* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load64(
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
; CHECK: call void @__hwasan_load8(i64 %[[A]])
; CHECK: %[[B:[^ ]*]] = load i64, i64* %a
; CHECK: ret i64 %[[B]]
entry:
%b = load i64, i64* %a, align 8
ret i64 %b
}
define i128 @test_load128(i128* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load128(
; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64
; CHECK: call void @__hwasan_load16(i64 %[[A]])
; CHECK: %[[B:[^ ]*]] = load i128, i128* %a
; CHECK: ret i128 %[[B]]
entry:
%b = load i128, i128* %a, align 16
ret i128 %b
}
define i40 @test_load40(i40* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load40(
; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
; CHECK: call void @__hwasan_load(i64 %[[A]], i64 5)
; CHECK: %[[B:[^ ]*]] = load i40, i40* %a
; CHECK: ret i40 %[[B]]
entry:
%b = load i40, i40* %a, align 4
ret i40 %b
}
define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress {
; CHECK-LABEL: @test_store8(
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: call void @__hwasan_store1(i64 %[[A]])
; CHECK: store i8 %b, i8* %a
; CHECK: ret void
entry:
store i8 %b, i8* %a, align 4
ret void
}
define void @test_store16(i16* %a, i16 %b) sanitize_hwaddress {
; CHECK-LABEL: @test_store16(
; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64
; CHECK: call void @__hwasan_store2(i64 %[[A]])
; CHECK: store i16 %b, i16* %a
; CHECK: ret void
entry:
store i16 %b, i16* %a, align 4
ret void
}
define void @test_store32(i32* %a, i32 %b) sanitize_hwaddress {
; CHECK-LABEL: @test_store32(
; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64
; CHECK: call void @__hwasan_store4(i64 %[[A]])
; CHECK: store i32 %b, i32* %a
; CHECK: ret void
entry:
store i32 %b, i32* %a, align 4
ret void
}
define void @test_store64(i64* %a, i64 %b) sanitize_hwaddress {
; CHECK-LABEL: @test_store64(
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
; CHECK: call void @__hwasan_store8(i64 %[[A]])
; CHECK: store i64 %b, i64* %a
; CHECK: ret void
entry:
store i64 %b, i64* %a, align 4
ret void
}
define void @test_store128(i128* %a, i128 %b) sanitize_hwaddress {
; CHECK-LABEL: @test_store128(
; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64
; CHECK: call void @__hwasan_store16(i64 %[[A]])
; CHECK: store i128 %b, i128* %a
; CHECK: ret void
entry:
store i128 %b, i128* %a, align 4
ret void
}
define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress {
; CHECK-LABEL: @test_store40(
; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
; CHECK: call void @__hwasan_store(i64 %[[A]], i64 5)
; CHECK: store i40 %b, i40* %a
; CHECK: ret void
entry:
store i40 %b, i40* %a, align 4
ret void
}
define i8 @test_load_noattr(i8* %a) {
; CHECK-LABEL: @test_load_noattr(
; CHECK-NEXT: entry:
; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a
; CHECK-NEXT: ret i8 %[[B]]
entry:
%b = load i8, i8* %a, align 4
ret i8 %b
}
define i8 @test_load_notmyattr(i8* %a) sanitize_address {
; CHECK-LABEL: @test_load_notmyattr(
; CHECK-NEXT: entry:
; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a
; CHECK-NEXT: ret i8 %[[B]]
entry:
%b = load i8, i8* %a, align 4
ret i8 %b
}
define i8 @test_load_addrspace(i8 addrspace(256)* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load_addrspace(
; CHECK-NEXT: entry:
; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8 addrspace(256)* %a
; CHECK-NEXT: ret i8 %[[B]]
entry:
%b = load i8, i8 addrspace(256)* %a, align 4
ret i8 %b
}
; CHECK: declare void @__hwasan_init()
; CHECK: define internal void @hwasan.module_ctor() {
; CHECK-NEXT: call void @__hwasan_init()
; CHECK-NEXT: ret void
; CHECK-NEXT: }

View File

@ -53,3 +53,30 @@ define i32 @TestAsan() sanitize_address {
; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32 ; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
; CHECK: {{.*}} = phi ; CHECK: {{.*}} = phi
define i32 @TestHWAsan() sanitize_hwaddress {
%1 = tail call noalias i8* @_Znam(i64 2)
%2 = getelementptr inbounds i8, i8* %1, i64 1
store i8 0, i8* %2, align 1
store i8 0, i8* %1, align 1
%3 = bitcast i8* %1 to i16*
%4 = load i16, i16* %3, align 4
%5 = icmp eq i16 %4, 0
br i1 %5, label %11, label %6
; <label>:6 ; preds = %0
%7 = getelementptr inbounds i8, i8* %1, i64 2
%8 = bitcast i8* %7 to i16*
%9 = load i16, i16* %8, align 2
%10 = sext i16 %9 to i32
br label %11
; <label>:11 ; preds = %0, %6
%12 = phi i32 [ %10, %6 ], [ 0, %0 ]
ret i32 %12
}
; CHECK-LABEL: @TestHWAsan
; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
; CHECK: {{.*}} = phi

View File

@ -10,6 +10,10 @@ define i32 @sanitize_address_callee(i32 %i) sanitize_address {
ret i32 %i ret i32 %i
} }
define i32 @sanitize_hwaddress_callee(i32 %i) sanitize_hwaddress {
ret i32 %i
}
define i32 @sanitize_thread_callee(i32 %i) sanitize_thread { define i32 @sanitize_thread_callee(i32 %i) sanitize_thread {
ret i32 %i ret i32 %i
} }
@ -30,6 +34,10 @@ define i32 @alwaysinline_sanitize_address_callee(i32 %i) alwaysinline sanitize_a
ret i32 %i ret i32 %i
} }
define i32 @alwaysinline_sanitize_hwaddress_callee(i32 %i) alwaysinline sanitize_hwaddress {
ret i32 %i
}
define i32 @alwaysinline_sanitize_thread_callee(i32 %i) alwaysinline sanitize_thread { define i32 @alwaysinline_sanitize_thread_callee(i32 %i) alwaysinline sanitize_thread {
ret i32 %i ret i32 %i
} }
@ -59,6 +67,17 @@ define i32 @test_no_sanitize_address(i32 %arg) {
; CHECK-NEXT: ret i32 ; CHECK-NEXT: ret i32
} }
define i32 @test_no_sanitize_hwaddress(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_no_sanitize_hwaddress(
; CHECK-NEXT: @sanitize_hwaddress_callee
; CHECK-NEXT: ret i32
}
define i32 @test_no_sanitize_memory(i32 %arg) { define i32 @test_no_sanitize_memory(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg) %x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memory_callee(i32 %x1) %x2 = call i32 @sanitize_memory_callee(i32 %x1)
@ -98,6 +117,17 @@ define i32 @test_sanitize_address(i32 %arg) sanitize_address {
; CHECK-NEXT: ret i32 ; CHECK-NEXT: ret i32
} }
define i32 @test_sanitize_hwaddress(i32 %arg) sanitize_hwaddress {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_sanitize_hwaddress(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
define i32 @test_sanitize_memory(i32 %arg) sanitize_memory { define i32 @test_sanitize_memory(i32 %arg) sanitize_memory {
%x1 = call i32 @noattr_callee(i32 %arg) %x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memory_callee(i32 %x1) %x2 = call i32 @sanitize_memory_callee(i32 %x1)

View File

@ -19,6 +19,20 @@ entry:
ret void ret void
} }
define void @hwasan() sanitize_hwaddress {
entry:
; CHECK-LABEL: @hwasan(
%text = alloca i8, align 1
call void @llvm.lifetime.start.p0i8(i64 1, i8* %text)
call void @llvm.lifetime.end.p0i8(i64 1, i8* %text)
; CHECK: call void @llvm.lifetime.start
; CHECK-NEXT: call void @llvm.lifetime.end
call void @foo(i8* %text) ; Keep alloca alive
ret void
}
define void @no_asan() { define void @no_asan() {
entry: entry:

View File

@ -53,3 +53,29 @@ define i32 @TestAsan() sanitize_address {
; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32 ; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
; CHECK: {{.*}} = phi ; CHECK: {{.*}} = phi
define i32 @TestHWAsan() sanitize_hwaddress {
%1 = tail call noalias i8* @_Znam(i64 2)
%2 = getelementptr inbounds i8, i8* %1, i64 1
store i8 0, i8* %2, align 1
store i8 0, i8* %1, align 1
%3 = bitcast i8* %1 to i16*
%4 = load i16, i16* %3, align 4
%5 = icmp eq i16 %4, 0
br i1 %5, label %11, label %6
; <label>:6 ; preds = %0
%7 = getelementptr inbounds i8, i8* %1, i64 2
%8 = bitcast i8* %7 to i16*
%9 = load i16, i16* %8, align 2
%10 = sext i16 %9 to i32
br label %11
; <label>:11 ; preds = %0, %6
%12 = phi i32 [ %10, %6 ], [ 0, %0 ]
ret i32 %12
}
; CHECK-LABEL: @TestHWAsan
; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
; CHECK: {{.*}} = phi

View File

@ -38,3 +38,22 @@ return: ; preds = %entry, %if.then
; CHECK: br label ; CHECK: br label
; CHECK: ret i32 ; CHECK: ret i32
} }
define i32 @TestHWAsan(i32 %cond) nounwind readonly uwtable sanitize_hwaddress {
entry:
%tobool = icmp eq i32 %cond, 0
br i1 %tobool, label %return, label %if.then
if.then: ; preds = %entry
%0 = load i32, i32* @g, align 4
br label %return
return: ; preds = %entry, %if.then
%retval = phi i32 [ %0, %if.then ], [ 0, %entry ]
ret i32 %retval
; CHECK-LABEL: @TestHWAsan
; CHECK: br i1
; CHECK: load i32, i32* @g
; CHECK: br label
; CHECK: ret i32
}