1 //===- HWAddressSanitizer.cpp - detector of uninitialized reads -------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// This file is a part of HWAddressSanitizer, an address sanity checker
12 /// based on tagged addressing.
13 //===----------------------------------------------------------------------===//
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/ADT/Triple.h"
19 #include "llvm/IR/Attributes.h"
20 #include "llvm/IR/BasicBlock.h"
21 #include "llvm/IR/Constant.h"
22 #include "llvm/IR/Constants.h"
23 #include "llvm/IR/DataLayout.h"
24 #include "llvm/IR/DerivedTypes.h"
25 #include "llvm/IR/MDBuilder.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include "llvm/IR/Function.h"
28 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
29 #include "llvm/IR/IRBuilder.h"
30 #include "llvm/IR/InlineAsm.h"
31 #include "llvm/IR/InstVisitor.h"
32 #include "llvm/IR/Instruction.h"
33 #include "llvm/IR/Instructions.h"
34 #include "llvm/IR/IntrinsicInst.h"
35 #include "llvm/IR/Intrinsics.h"
36 #include "llvm/IR/LLVMContext.h"
37 #include "llvm/IR/Module.h"
38 #include "llvm/IR/Type.h"
39 #include "llvm/IR/Value.h"
40 #include "llvm/Pass.h"
41 #include "llvm/Support/Casting.h"
42 #include "llvm/Support/CommandLine.h"
43 #include "llvm/Support/Debug.h"
44 #include "llvm/Transforms/Instrumentation.h"
45 #include "llvm/Transforms/Utils/ModuleUtils.h"
49 #define DEBUG_TYPE "hwasan"
51 static const char *const kHwasanModuleCtorName = "hwasan.module_ctor";
52 static const char *const kHwasanInitName = "__hwasan_init";
54 // Accesses sizes are powers of two: 1, 2, 4, 8, 16.
55 static const size_t kNumberOfAccessSizes = 5;
57 static const size_t kShadowScale = 4;
58 static const unsigned kPointerTagShift = 56;
60 static cl::opt<std::string> ClMemoryAccessCallbackPrefix(
61 "hwasan-memory-access-callback-prefix",
62 cl::desc("Prefix for memory access callbacks"), cl::Hidden,
63 cl::init("__hwasan_"));
66 ClInstrumentWithCalls("hwasan-instrument-with-calls",
67 cl::desc("instrument reads and writes with callbacks"),
68 cl::Hidden, cl::init(false));
70 static cl::opt<bool> ClInstrumentReads("hwasan-instrument-reads",
71 cl::desc("instrument read instructions"),
72 cl::Hidden, cl::init(true));
74 static cl::opt<bool> ClInstrumentWrites(
75 "hwasan-instrument-writes", cl::desc("instrument write instructions"),
76 cl::Hidden, cl::init(true));
78 static cl::opt<bool> ClInstrumentAtomics(
79 "hwasan-instrument-atomics",
80 cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
83 static cl::opt<bool> ClRecover(
85 cl::desc("Enable recovery mode (continue-after-error)."),
86 cl::Hidden, cl::init(false));
90 /// \brief An instrumentation pass implementing detection of addressability bugs
91 /// using tagged pointers.
92 class HWAddressSanitizer : public FunctionPass {
94 // Pass identification, replacement for typeid.
97 HWAddressSanitizer(bool Recover = false)
98 : FunctionPass(ID), Recover(Recover || ClRecover) {}
100 StringRef getPassName() const override { return "HWAddressSanitizer"; }
102 bool runOnFunction(Function &F) override;
103 bool doInitialization(Module &M) override;
105 void initializeCallbacks(Module &M);
106 void instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
107 unsigned AccessSizeIndex,
108 Instruction *InsertBefore);
109 bool instrumentMemAccess(Instruction *I);
110 Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
111 uint64_t *TypeSize, unsigned *Alignment,
120 Function *HwasanCtorFunction;
122 Function *HwasanMemoryAccessCallback[2][kNumberOfAccessSizes];
123 Function *HwasanMemoryAccessCallbackSized[2];
126 } // end anonymous namespace
128 char HWAddressSanitizer::ID = 0;
130 INITIALIZE_PASS_BEGIN(
131 HWAddressSanitizer, "hwasan",
132 "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
134 HWAddressSanitizer, "hwasan",
135 "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
137 FunctionPass *llvm::createHWAddressSanitizerPass(bool Recover) {
138 return new HWAddressSanitizer(Recover);
141 /// \brief Module-level initialization.
143 /// inserts a call to __hwasan_init to the module's constructor list.
144 bool HWAddressSanitizer::doInitialization(Module &M) {
145 DEBUG(dbgs() << "Init " << M.getName() << "\n");
146 auto &DL = M.getDataLayout();
148 Triple TargetTriple(M.getTargetTriple());
150 C = &(M.getContext());
152 IntptrTy = IRB.getIntPtrTy(DL);
154 std::tie(HwasanCtorFunction, std::ignore) =
155 createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName,
159 appendToGlobalCtors(M, HwasanCtorFunction, 0);
163 void HWAddressSanitizer::initializeCallbacks(Module &M) {
165 for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) {
166 const std::string TypeStr = AccessIsWrite ? "store" : "load";
167 const std::string EndingStr = Recover ? "_noabort" : "";
169 HwasanMemoryAccessCallbackSized[AccessIsWrite] =
170 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
171 ClMemoryAccessCallbackPrefix + TypeStr + EndingStr,
172 FunctionType::get(IRB.getVoidTy(), {IntptrTy, IntptrTy}, false)));
174 for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
176 HwasanMemoryAccessCallback[AccessIsWrite][AccessSizeIndex] =
177 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
178 ClMemoryAccessCallbackPrefix + TypeStr +
179 itostr(1ULL << AccessSizeIndex) + EndingStr,
180 FunctionType::get(IRB.getVoidTy(), {IntptrTy}, false)));
185 Value *HWAddressSanitizer::isInterestingMemoryAccess(Instruction *I,
190 // Skip memory accesses inserted by another instrumentation.
191 if (I->getMetadata("nosanitize")) return nullptr;
193 Value *PtrOperand = nullptr;
194 const DataLayout &DL = I->getModule()->getDataLayout();
195 if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
196 if (!ClInstrumentReads) return nullptr;
198 *TypeSize = DL.getTypeStoreSizeInBits(LI->getType());
199 *Alignment = LI->getAlignment();
200 PtrOperand = LI->getPointerOperand();
201 } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
202 if (!ClInstrumentWrites) return nullptr;
204 *TypeSize = DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType());
205 *Alignment = SI->getAlignment();
206 PtrOperand = SI->getPointerOperand();
207 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
208 if (!ClInstrumentAtomics) return nullptr;
210 *TypeSize = DL.getTypeStoreSizeInBits(RMW->getValOperand()->getType());
212 PtrOperand = RMW->getPointerOperand();
213 } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
214 if (!ClInstrumentAtomics) return nullptr;
216 *TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
218 PtrOperand = XCHG->getPointerOperand();
222 // Do not instrument acesses from different address spaces; we cannot deal
224 Type *PtrTy = cast<PointerType>(PtrOperand->getType()->getScalarType());
225 if (PtrTy->getPointerAddressSpace() != 0)
228 // Ignore swifterror addresses.
229 // swifterror memory addresses are mem2reg promoted by instruction
230 // selection. As such they cannot have regular uses like an instrumentation
231 // function and it makes no sense to track them as memory.
232 if (PtrOperand->isSwiftError())
239 static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
240 size_t Res = countTrailingZeros(TypeSize / 8);
241 assert(Res < kNumberOfAccessSizes);
245 void HWAddressSanitizer::instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
246 unsigned AccessSizeIndex,
247 Instruction *InsertBefore) {
248 IRBuilder<> IRB(InsertBefore);
249 Value *PtrTag = IRB.CreateTrunc(IRB.CreateLShr(PtrLong, kPointerTagShift), IRB.getInt8Ty());
251 IRB.CreateAnd(PtrLong, ConstantInt::get(PtrLong->getType(),
252 ~(0xFFULL << kPointerTagShift)));
253 Value *ShadowLong = IRB.CreateLShr(AddrLong, kShadowScale);
254 Value *MemTag = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowLong, IRB.getInt8PtrTy()));
255 Value *TagMismatch = IRB.CreateICmpNE(PtrTag, MemTag);
257 TerminatorInst *CheckTerm =
258 SplitBlockAndInsertIfThen(TagMismatch, InsertBefore, !Recover,
259 MDBuilder(*C).createBranchWeights(1, 100000));
261 IRB.SetInsertPoint(CheckTerm);
262 // The signal handler will find the data address in x0.
263 InlineAsm *Asm = InlineAsm::get(
264 FunctionType::get(IRB.getVoidTy(), {PtrLong->getType()}, false),
266 itostr(0x100 + Recover * 0x20 + IsWrite * 0x10 + AccessSizeIndex),
268 /*hasSideEffects=*/true);
269 IRB.CreateCall(Asm, PtrLong);
272 bool HWAddressSanitizer::instrumentMemAccess(Instruction *I) {
273 DEBUG(dbgs() << "Instrumenting: " << *I << "\n");
274 bool IsWrite = false;
275 unsigned Alignment = 0;
276 uint64_t TypeSize = 0;
277 Value *MaybeMask = nullptr;
279 isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask);
285 return false; //FIXME
288 Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
289 if (isPowerOf2_64(TypeSize) &&
290 (TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1))) &&
291 (Alignment >= (1UL << kShadowScale) || Alignment == 0 ||
292 Alignment >= TypeSize / 8)) {
293 size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
294 if (ClInstrumentWithCalls) {
295 IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex],
298 instrumentMemAccessInline(AddrLong, IsWrite, AccessSizeIndex, I);
301 IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite],
302 {AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)});
308 bool HWAddressSanitizer::runOnFunction(Function &F) {
309 if (&F == HwasanCtorFunction)
312 if (!F.hasFnAttribute(Attribute::SanitizeHWAddress))
315 DEBUG(dbgs() << "Function: " << F.getName() << "\n");
317 initializeCallbacks(*F.getParent());
319 bool Changed = false;
320 SmallVector<Instruction*, 16> ToInstrument;
322 for (auto &Inst : BB) {
323 Value *MaybeMask = nullptr;
327 Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize,
328 &Alignment, &MaybeMask);
329 if (Addr || isa<MemIntrinsic>(Inst))
330 ToInstrument.push_back(&Inst);
334 for (auto Inst : ToInstrument)
335 Changed |= instrumentMemAccess(Inst);