blob: 8e2833d2203222e85d4e2dfed3e4b733f768d65a [file] [log] [blame]
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +00001//===- HWAddressSanitizer.cpp - detector of uninitialized reads -------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10/// \file
11/// This file is a part of HWAddressSanitizer, an address sanity checker
12/// based on tagged addressing.
13//===----------------------------------------------------------------------===//
14
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"
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000025#include "llvm/IR/MDBuilder.h"
26#include "llvm/Support/raw_ostream.h"
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000027#include "llvm/IR/Function.h"
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000028#include "llvm/Transforms/Utils/BasicBlockUtils.h"
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000029#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"
46
47using namespace llvm;
48
49#define DEBUG_TYPE "hwasan"
50
51static const char *const kHwasanModuleCtorName = "hwasan.module_ctor";
52static const char *const kHwasanInitName = "__hwasan_init";
53
54// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
55static const size_t kNumberOfAccessSizes = 5;
56
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000057static const size_t kShadowScale = 4;
58static const unsigned kPointerTagShift = 56;
59
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000060static 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_"));
64
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000065static cl::opt<bool>
66 ClInstrumentWithCalls("hwasan-instrument-with-calls",
67 cl::desc("instrument reads and writes with callbacks"),
68 cl::Hidden, cl::init(false));
69
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000070static cl::opt<bool> ClInstrumentReads("hwasan-instrument-reads",
71 cl::desc("instrument read instructions"),
72 cl::Hidden, cl::init(true));
73
74static cl::opt<bool> ClInstrumentWrites(
75 "hwasan-instrument-writes", cl::desc("instrument write instructions"),
76 cl::Hidden, cl::init(true));
77
78static cl::opt<bool> ClInstrumentAtomics(
79 "hwasan-instrument-atomics",
80 cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
81 cl::init(true));
82
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +000083static cl::opt<bool> ClRecover(
84 "hwasan-recover",
85 cl::desc("Enable recovery mode (continue-after-error)."),
86 cl::Hidden, cl::init(false));
87
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000088namespace {
89
90/// \brief An instrumentation pass implementing detection of addressability bugs
91/// using tagged pointers.
92class HWAddressSanitizer : public FunctionPass {
93public:
94 // Pass identification, replacement for typeid.
95 static char ID;
96
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +000097 HWAddressSanitizer(bool Recover = false)
98 : FunctionPass(ID), Recover(Recover || ClRecover) {}
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000099
100 StringRef getPassName() const override { return "HWAddressSanitizer"; }
101
102 bool runOnFunction(Function &F) override;
103 bool doInitialization(Module &M) override;
104
105 void initializeCallbacks(Module &M);
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000106 void instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
107 unsigned AccessSizeIndex,
108 Instruction *InsertBefore);
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000109 bool instrumentMemAccess(Instruction *I);
110 Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
111 uint64_t *TypeSize, unsigned *Alignment,
112 Value **MaybeMask);
113
114private:
115 LLVMContext *C;
116 Type *IntptrTy;
117
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000118 bool Recover;
119
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000120 Function *HwasanCtorFunction;
121
122 Function *HwasanMemoryAccessCallback[2][kNumberOfAccessSizes];
123 Function *HwasanMemoryAccessCallbackSized[2];
124};
125
126} // end anonymous namespace
127
128char HWAddressSanitizer::ID = 0;
129
130INITIALIZE_PASS_BEGIN(
131 HWAddressSanitizer, "hwasan",
132 "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
133INITIALIZE_PASS_END(
134 HWAddressSanitizer, "hwasan",
135 "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
136
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000137FunctionPass *llvm::createHWAddressSanitizerPass(bool Recover) {
138 return new HWAddressSanitizer(Recover);
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000139}
140
141/// \brief Module-level initialization.
142///
143/// inserts a call to __hwasan_init to the module's constructor list.
144bool HWAddressSanitizer::doInitialization(Module &M) {
145 DEBUG(dbgs() << "Init " << M.getName() << "\n");
146 auto &DL = M.getDataLayout();
147
148 Triple TargetTriple(M.getTargetTriple());
149
150 C = &(M.getContext());
151 IRBuilder<> IRB(*C);
152 IntptrTy = IRB.getIntPtrTy(DL);
153
154 std::tie(HwasanCtorFunction, std::ignore) =
155 createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName,
156 kHwasanInitName,
157 /*InitArgTypes=*/{},
158 /*InitArgs=*/{});
159 appendToGlobalCtors(M, HwasanCtorFunction, 0);
160 return true;
161}
162
163void HWAddressSanitizer::initializeCallbacks(Module &M) {
164 IRBuilder<> IRB(*C);
165 for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) {
166 const std::string TypeStr = AccessIsWrite ? "store" : "load";
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000167 const std::string EndingStr = Recover ? "_noabort" : "";
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000168
169 HwasanMemoryAccessCallbackSized[AccessIsWrite] =
170 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000171 ClMemoryAccessCallbackPrefix + TypeStr + EndingStr,
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000172 FunctionType::get(IRB.getVoidTy(), {IntptrTy, IntptrTy}, false)));
173
174 for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
175 AccessSizeIndex++) {
176 HwasanMemoryAccessCallback[AccessIsWrite][AccessSizeIndex] =
177 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
178 ClMemoryAccessCallbackPrefix + TypeStr +
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000179 itostr(1ULL << AccessSizeIndex) + EndingStr,
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000180 FunctionType::get(IRB.getVoidTy(), {IntptrTy}, false)));
181 }
182 }
183}
184
185Value *HWAddressSanitizer::isInterestingMemoryAccess(Instruction *I,
186 bool *IsWrite,
187 uint64_t *TypeSize,
188 unsigned *Alignment,
189 Value **MaybeMask) {
190 // Skip memory accesses inserted by another instrumentation.
191 if (I->getMetadata("nosanitize")) return nullptr;
192
193 Value *PtrOperand = nullptr;
194 const DataLayout &DL = I->getModule()->getDataLayout();
195 if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
196 if (!ClInstrumentReads) return nullptr;
197 *IsWrite = false;
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;
203 *IsWrite = true;
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;
209 *IsWrite = true;
210 *TypeSize = DL.getTypeStoreSizeInBits(RMW->getValOperand()->getType());
211 *Alignment = 0;
212 PtrOperand = RMW->getPointerOperand();
213 } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
214 if (!ClInstrumentAtomics) return nullptr;
215 *IsWrite = true;
216 *TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
217 *Alignment = 0;
218 PtrOperand = XCHG->getPointerOperand();
219 }
220
221 if (PtrOperand) {
222 // Do not instrument acesses from different address spaces; we cannot deal
223 // with them.
224 Type *PtrTy = cast<PointerType>(PtrOperand->getType()->getScalarType());
225 if (PtrTy->getPointerAddressSpace() != 0)
226 return nullptr;
227
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())
233 return nullptr;
234 }
235
236 return PtrOperand;
237}
238
239static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
240 size_t Res = countTrailingZeros(TypeSize / 8);
241 assert(Res < kNumberOfAccessSizes);
242 return Res;
243}
244
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000245void 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());
250 Value *AddrLong =
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);
256
257 TerminatorInst *CheckTerm =
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000258 SplitBlockAndInsertIfThen(TagMismatch, InsertBefore, !Recover,
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000259 MDBuilder(*C).createBranchWeights(1, 100000));
260
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),
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000265 "hlt #" +
266 itostr(0x100 + Recover * 0x20 + IsWrite * 0x10 + AccessSizeIndex),
267 "{x0}",
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000268 /*hasSideEffects=*/true);
269 IRB.CreateCall(Asm, PtrLong);
270}
271
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000272bool 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;
278 Value *Addr =
279 isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask);
280
281 if (!Addr)
282 return false;
283
284 if (MaybeMask)
285 return false; //FIXME
286
287 IRBuilder<> IRB(I);
288 Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
289 if (isPowerOf2_64(TypeSize) &&
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000290 (TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1))) &&
291 (Alignment >= (1UL << kShadowScale) || Alignment == 0 ||
292 Alignment >= TypeSize / 8)) {
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000293 size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000294 if (ClInstrumentWithCalls) {
295 IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex],
296 AddrLong);
297 } else {
298 instrumentMemAccessInline(AddrLong, IsWrite, AccessSizeIndex, I);
299 }
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000300 } else {
301 IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite],
302 {AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)});
303 }
304
305 return true;
306}
307
308bool HWAddressSanitizer::runOnFunction(Function &F) {
309 if (&F == HwasanCtorFunction)
310 return false;
311
312 if (!F.hasFnAttribute(Attribute::SanitizeHWAddress))
313 return false;
314
315 DEBUG(dbgs() << "Function: " << F.getName() << "\n");
316
317 initializeCallbacks(*F.getParent());
318
319 bool Changed = false;
320 SmallVector<Instruction*, 16> ToInstrument;
321 for (auto &BB : F) {
322 for (auto &Inst : BB) {
323 Value *MaybeMask = nullptr;
324 bool IsWrite;
325 unsigned Alignment;
326 uint64_t TypeSize;
327 Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize,
328 &Alignment, &MaybeMask);
329 if (Addr || isa<MemIntrinsic>(Inst))
330 ToInstrument.push_back(&Inst);
331 }
332 }
333
334 for (auto Inst : ToInstrument)
335 Changed |= instrumentMemAccess(Inst);
336
337 return Changed;
338}