mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +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:
parent
716568d4d2
commit
67168a732b
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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> {
|
||||||
|
@ -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&);
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -306,8 +306,10 @@ unsigned MemoryDependenceResults::getLoadLoadClobberFullWidthSize(
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (LIOffs + NewLoadByteSize > MemLocEnd &&
|
if (LIOffs + NewLoadByteSize > MemLocEnd &&
|
||||||
|
(LI->getParent()->getParent()->hasFnAttribute(
|
||||||
|
Attribute::SanitizeAddress) ||
|
||||||
LI->getParent()->getParent()->hasFnAttribute(
|
LI->getParent()->getParent()->hasFnAttribute(
|
||||||
Attribute::SanitizeAddress))
|
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.
|
||||||
|
@ -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(),
|
||||||
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -567,7 +567,8 @@ bool ShrinkWrap::isShrinkWrapEnabled(const MachineFunction &MF) {
|
|||||||
// 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.
|
||||||
|
@ -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))
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
282
lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Normal file
282
lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Normal 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;
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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 }
|
||||||
|
26
test/Instrumentation/HWAddressSanitizer/atomic.ll
Normal file
26
test/Instrumentation/HWAddressSanitizer/atomic.ll
Normal 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
|
||||||
|
}
|
190
test/Instrumentation/HWAddressSanitizer/basic.ll
Normal file
190
test/Instrumentation/HWAddressSanitizer/basic.ll
Normal 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: }
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user