blob: 21541fc90295a59b51d7208c939c57699191b45f [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"
25#include "llvm/IR/Function.h"
26#include "llvm/IR/IRBuilder.h"
27#include "llvm/IR/InlineAsm.h"
28#include "llvm/IR/InstVisitor.h"
29#include "llvm/IR/Instruction.h"
30#include "llvm/IR/Instructions.h"
31#include "llvm/IR/IntrinsicInst.h"
32#include "llvm/IR/Intrinsics.h"
33#include "llvm/IR/LLVMContext.h"
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +000034#include "llvm/IR/MDBuilder.h"
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000035#include "llvm/IR/Module.h"
36#include "llvm/IR/Type.h"
37#include "llvm/IR/Value.h"
38#include "llvm/Pass.h"
39#include "llvm/Support/Casting.h"
40#include "llvm/Support/CommandLine.h"
41#include "llvm/Support/Debug.h"
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +000042#include "llvm/Support/raw_ostream.h"
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000043#include "llvm/Transforms/Instrumentation.h"
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +000044#include "llvm/Transforms/Utils/BasicBlockUtils.h"
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000045#include "llvm/Transforms/Utils/ModuleUtils.h"
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +000046#include "llvm/Transforms/Utils/PromoteMemToReg.h"
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000047
48using namespace llvm;
49
50#define DEBUG_TYPE "hwasan"
51
52static const char *const kHwasanModuleCtorName = "hwasan.module_ctor";
53static const char *const kHwasanInitName = "__hwasan_init";
54
55// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
56static const size_t kNumberOfAccessSizes = 5;
57
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000058static const size_t kShadowScale = 4;
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +000059static const unsigned kAllocaAlignment = 1U << kShadowScale;
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000060static const unsigned kPointerTagShift = 56;
61
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000062static cl::opt<std::string> ClMemoryAccessCallbackPrefix(
63 "hwasan-memory-access-callback-prefix",
64 cl::desc("Prefix for memory access callbacks"), cl::Hidden,
65 cl::init("__hwasan_"));
66
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +000067static cl::opt<bool>
68 ClInstrumentWithCalls("hwasan-instrument-with-calls",
69 cl::desc("instrument reads and writes with callbacks"),
70 cl::Hidden, cl::init(false));
71
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +000072static cl::opt<bool> ClInstrumentReads("hwasan-instrument-reads",
73 cl::desc("instrument read instructions"),
74 cl::Hidden, cl::init(true));
75
76static cl::opt<bool> ClInstrumentWrites(
77 "hwasan-instrument-writes", cl::desc("instrument write instructions"),
78 cl::Hidden, cl::init(true));
79
80static cl::opt<bool> ClInstrumentAtomics(
81 "hwasan-instrument-atomics",
82 cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
83 cl::init(true));
84
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +000085static cl::opt<bool> ClRecover(
86 "hwasan-recover",
87 cl::desc("Enable recovery mode (continue-after-error)."),
88 cl::Hidden, cl::init(false));
89
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +000090static cl::opt<bool> ClInstrumentStack("hwasan-instrument-stack",
91 cl::desc("instrument stack (allocas)"),
92 cl::Hidden, cl::init(true));
93
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +000094static cl::opt<bool> ClGenerateTagsWithCalls(
95 "hwasan-generate-tags-with-calls",
96 cl::desc("generate new tags with runtime library calls"), cl::Hidden,
97 cl::init(false));
98
Evgeniy Stepanov5bd669d2018-01-17 23:24:38 +000099static cl::opt<unsigned long long> ClMappingOffset(
100 "hwasan-mapping-offset",
101 cl::desc("offset of hwasan shadow mapping [EXPERIMENTAL]"), cl::Hidden,
102 cl::init(0));
103
104static cl::opt<bool> ClEnableKhwasan(
105 "hwasan-kernel", cl::desc("Enable KernelHWAddressSanitizer instrumentation"),
106 cl::Hidden, cl::init(false));
107
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000108namespace {
109
110/// \brief An instrumentation pass implementing detection of addressability bugs
111/// using tagged pointers.
112class HWAddressSanitizer : public FunctionPass {
113public:
114 // Pass identification, replacement for typeid.
115 static char ID;
116
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000117 HWAddressSanitizer(bool Recover = false)
118 : FunctionPass(ID), Recover(Recover || ClRecover) {}
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000119
120 StringRef getPassName() const override { return "HWAddressSanitizer"; }
121
122 bool runOnFunction(Function &F) override;
123 bool doInitialization(Module &M) override;
124
125 void initializeCallbacks(Module &M);
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000126 void instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
127 unsigned AccessSizeIndex,
128 Instruction *InsertBefore);
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000129 bool instrumentMemAccess(Instruction *I);
130 Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
131 uint64_t *TypeSize, unsigned *Alignment,
132 Value **MaybeMask);
133
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000134 bool isInterestingAlloca(const AllocaInst &AI);
135 bool tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag);
Evgeniy Stepanov80ccda22018-02-09 00:59:10 +0000136 Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag);
Evgeniy Stepanov43271b12018-02-21 19:52:23 +0000137 Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong);
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000138 bool instrumentStack(SmallVectorImpl<AllocaInst *> &Allocas,
139 SmallVectorImpl<Instruction *> &RetVec);
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000140 Value *getNextTagWithCall(IRBuilder<> &IRB);
141 Value *getStackBaseTag(IRBuilder<> &IRB);
142 Value *getAllocaTag(IRBuilder<> &IRB, Value *StackTag, AllocaInst *AI,
143 unsigned AllocaNo);
144 Value *getUARTag(IRBuilder<> &IRB, Value *StackTag);
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000145
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000146private:
147 LLVMContext *C;
148 Type *IntptrTy;
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000149 Type *Int8Ty;
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000150
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000151 bool Recover;
152
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000153 Function *HwasanCtorFunction;
154
155 Function *HwasanMemoryAccessCallback[2][kNumberOfAccessSizes];
156 Function *HwasanMemoryAccessCallbackSized[2];
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000157
158 Function *HwasanTagMemoryFunc;
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000159 Function *HwasanGenerateTagFunc;
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000160};
161
162} // end anonymous namespace
163
164char HWAddressSanitizer::ID = 0;
165
166INITIALIZE_PASS_BEGIN(
167 HWAddressSanitizer, "hwasan",
168 "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
169INITIALIZE_PASS_END(
170 HWAddressSanitizer, "hwasan",
171 "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false)
172
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000173FunctionPass *llvm::createHWAddressSanitizerPass(bool Recover) {
174 return new HWAddressSanitizer(Recover);
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000175}
176
177/// \brief Module-level initialization.
178///
179/// inserts a call to __hwasan_init to the module's constructor list.
180bool HWAddressSanitizer::doInitialization(Module &M) {
181 DEBUG(dbgs() << "Init " << M.getName() << "\n");
182 auto &DL = M.getDataLayout();
183
184 Triple TargetTriple(M.getTargetTriple());
185
186 C = &(M.getContext());
187 IRBuilder<> IRB(*C);
188 IntptrTy = IRB.getIntPtrTy(DL);
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000189 Int8Ty = IRB.getInt8Ty();
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000190
Benjamin Kramerbfc1d972018-01-18 14:19:04 +0000191 HwasanCtorFunction = nullptr;
Evgeniy Stepanov5bd669d2018-01-17 23:24:38 +0000192 if (!ClEnableKhwasan) {
193 std::tie(HwasanCtorFunction, std::ignore) =
194 createSanitizerCtorAndInitFunctions(M, kHwasanModuleCtorName,
195 kHwasanInitName,
196 /*InitArgTypes=*/{},
197 /*InitArgs=*/{});
198 appendToGlobalCtors(M, HwasanCtorFunction, 0);
199 }
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000200 return true;
201}
202
203void HWAddressSanitizer::initializeCallbacks(Module &M) {
204 IRBuilder<> IRB(*C);
205 for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) {
206 const std::string TypeStr = AccessIsWrite ? "store" : "load";
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000207 const std::string EndingStr = Recover ? "_noabort" : "";
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000208
209 HwasanMemoryAccessCallbackSized[AccessIsWrite] =
210 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
Evgeniy Stepanovc07e0bd2018-01-16 23:15:08 +0000211 ClMemoryAccessCallbackPrefix + TypeStr + "N" + EndingStr,
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000212 FunctionType::get(IRB.getVoidTy(), {IntptrTy, IntptrTy}, false)));
213
214 for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
215 AccessSizeIndex++) {
216 HwasanMemoryAccessCallback[AccessIsWrite][AccessSizeIndex] =
217 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
218 ClMemoryAccessCallbackPrefix + TypeStr +
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000219 itostr(1ULL << AccessSizeIndex) + EndingStr,
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000220 FunctionType::get(IRB.getVoidTy(), {IntptrTy}, false)));
221 }
222 }
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000223
224 HwasanTagMemoryFunc = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
225 "__hwasan_tag_memory", IRB.getVoidTy(), IntptrTy, Int8Ty, IntptrTy));
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000226 HwasanGenerateTagFunc = checkSanitizerInterfaceFunction(
227 M.getOrInsertFunction("__hwasan_generate_tag", Int8Ty));
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000228}
229
230Value *HWAddressSanitizer::isInterestingMemoryAccess(Instruction *I,
231 bool *IsWrite,
232 uint64_t *TypeSize,
233 unsigned *Alignment,
234 Value **MaybeMask) {
235 // Skip memory accesses inserted by another instrumentation.
236 if (I->getMetadata("nosanitize")) return nullptr;
237
238 Value *PtrOperand = nullptr;
239 const DataLayout &DL = I->getModule()->getDataLayout();
240 if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
241 if (!ClInstrumentReads) return nullptr;
242 *IsWrite = false;
243 *TypeSize = DL.getTypeStoreSizeInBits(LI->getType());
244 *Alignment = LI->getAlignment();
245 PtrOperand = LI->getPointerOperand();
246 } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
247 if (!ClInstrumentWrites) return nullptr;
248 *IsWrite = true;
249 *TypeSize = DL.getTypeStoreSizeInBits(SI->getValueOperand()->getType());
250 *Alignment = SI->getAlignment();
251 PtrOperand = SI->getPointerOperand();
252 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
253 if (!ClInstrumentAtomics) return nullptr;
254 *IsWrite = true;
255 *TypeSize = DL.getTypeStoreSizeInBits(RMW->getValOperand()->getType());
256 *Alignment = 0;
257 PtrOperand = RMW->getPointerOperand();
258 } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
259 if (!ClInstrumentAtomics) return nullptr;
260 *IsWrite = true;
261 *TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
262 *Alignment = 0;
263 PtrOperand = XCHG->getPointerOperand();
264 }
265
266 if (PtrOperand) {
267 // Do not instrument acesses from different address spaces; we cannot deal
268 // with them.
269 Type *PtrTy = cast<PointerType>(PtrOperand->getType()->getScalarType());
270 if (PtrTy->getPointerAddressSpace() != 0)
271 return nullptr;
272
273 // Ignore swifterror addresses.
274 // swifterror memory addresses are mem2reg promoted by instruction
275 // selection. As such they cannot have regular uses like an instrumentation
276 // function and it makes no sense to track them as memory.
277 if (PtrOperand->isSwiftError())
278 return nullptr;
279 }
280
281 return PtrOperand;
282}
283
284static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
285 size_t Res = countTrailingZeros(TypeSize / 8);
286 assert(Res < kNumberOfAccessSizes);
287 return Res;
288}
289
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000290void HWAddressSanitizer::instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
291 unsigned AccessSizeIndex,
292 Instruction *InsertBefore) {
293 IRBuilder<> IRB(InsertBefore);
294 Value *PtrTag = IRB.CreateTrunc(IRB.CreateLShr(PtrLong, kPointerTagShift), IRB.getInt8Ty());
Evgeniy Stepanov43271b12018-02-21 19:52:23 +0000295 Value *AddrLong = untagPointer(IRB, PtrLong);
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000296 Value *ShadowLong = IRB.CreateLShr(AddrLong, kShadowScale);
Evgeniy Stepanov5bd669d2018-01-17 23:24:38 +0000297 if (ClMappingOffset)
298 ShadowLong = IRB.CreateAdd(
299 ShadowLong, ConstantInt::get(PtrLong->getType(), ClMappingOffset,
300 /*isSigned=*/false));
301 Value *MemTag =
302 IRB.CreateLoad(IRB.CreateIntToPtr(ShadowLong, IRB.getInt8PtrTy()));
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000303 Value *TagMismatch = IRB.CreateICmpNE(PtrTag, MemTag);
304
305 TerminatorInst *CheckTerm =
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000306 SplitBlockAndInsertIfThen(TagMismatch, InsertBefore, !Recover,
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000307 MDBuilder(*C).createBranchWeights(1, 100000));
308
309 IRB.SetInsertPoint(CheckTerm);
310 // The signal handler will find the data address in x0.
311 InlineAsm *Asm = InlineAsm::get(
312 FunctionType::get(IRB.getVoidTy(), {PtrLong->getType()}, false),
Evgeniy Stepanov43271b12018-02-21 19:52:23 +0000313 "brk #" +
314 itostr(0x900 + Recover * 0x20 + IsWrite * 0x10 + AccessSizeIndex),
Evgeniy Stepanov3fd1b1a2017-12-20 19:05:44 +0000315 "{x0}",
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000316 /*hasSideEffects=*/true);
317 IRB.CreateCall(Asm, PtrLong);
318}
319
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000320bool HWAddressSanitizer::instrumentMemAccess(Instruction *I) {
321 DEBUG(dbgs() << "Instrumenting: " << *I << "\n");
322 bool IsWrite = false;
323 unsigned Alignment = 0;
324 uint64_t TypeSize = 0;
325 Value *MaybeMask = nullptr;
326 Value *Addr =
327 isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask);
328
329 if (!Addr)
330 return false;
331
332 if (MaybeMask)
333 return false; //FIXME
334
335 IRBuilder<> IRB(I);
336 Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
337 if (isPowerOf2_64(TypeSize) &&
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000338 (TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1))) &&
339 (Alignment >= (1UL << kShadowScale) || Alignment == 0 ||
340 Alignment >= TypeSize / 8)) {
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000341 size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
Evgeniy Stepanovecb48e52017-12-13 01:16:34 +0000342 if (ClInstrumentWithCalls) {
343 IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex],
344 AddrLong);
345 } else {
346 instrumentMemAccessInline(AddrLong, IsWrite, AccessSizeIndex, I);
347 }
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000348 } else {
349 IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite],
350 {AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)});
351 }
352
353 return true;
354}
355
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000356static uint64_t getAllocaSizeInBytes(const AllocaInst &AI) {
357 uint64_t ArraySize = 1;
358 if (AI.isArrayAllocation()) {
359 const ConstantInt *CI = dyn_cast<ConstantInt>(AI.getArraySize());
360 assert(CI && "non-constant array size");
361 ArraySize = CI->getZExtValue();
362 }
363 Type *Ty = AI.getAllocatedType();
364 uint64_t SizeInBytes = AI.getModule()->getDataLayout().getTypeAllocSize(Ty);
365 return SizeInBytes * ArraySize;
366}
367
368bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI,
369 Value *Tag) {
370 size_t Size = (getAllocaSizeInBytes(*AI) + kAllocaAlignment - 1) &
371 ~(kAllocaAlignment - 1);
372
373 Value *JustTag = IRB.CreateTrunc(Tag, IRB.getInt8Ty());
374 if (ClInstrumentWithCalls) {
375 IRB.CreateCall(HwasanTagMemoryFunc,
376 {IRB.CreatePointerCast(AI, IntptrTy), JustTag,
377 ConstantInt::get(IntptrTy, Size)});
378 } else {
379 size_t ShadowSize = Size >> kShadowScale;
380 Value *ShadowPtr = IRB.CreateIntToPtr(
381 IRB.CreateLShr(IRB.CreatePointerCast(AI, IntptrTy), kShadowScale),
382 IRB.getInt8PtrTy());
383 // If this memset is not inlined, it will be intercepted in the hwasan
384 // runtime library. That's OK, because the interceptor skips the checks if
385 // the address is in the shadow region.
386 // FIXME: the interceptor is not as fast as real memset. Consider lowering
387 // llvm.memset right here into either a sequence of stores, or a call to
388 // hwasan_tag_memory.
389 IRB.CreateMemSet(ShadowPtr, JustTag, ShadowSize, /*Align=*/1);
390 }
391 return true;
392}
393
394static unsigned RetagMask(unsigned AllocaNo) {
395 // A list of 8-bit numbers that have at most one run of non-zero bits.
396 // x = x ^ (mask << 56) can be encoded as a single armv8 instruction for these
397 // masks.
398 // The list does not include the value 255, which is used for UAR.
399 static unsigned FastMasks[] = {
400 0, 1, 2, 3, 4, 6, 7, 8, 12, 14, 15, 16, 24,
401 28, 30, 31, 32, 48, 56, 60, 62, 63, 64, 96, 112, 120,
402 124, 126, 127, 128, 192, 224, 240, 248, 252, 254};
403 return FastMasks[AllocaNo % (sizeof(FastMasks) / sizeof(FastMasks[0]))];
404}
405
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000406Value *HWAddressSanitizer::getNextTagWithCall(IRBuilder<> &IRB) {
407 return IRB.CreateZExt(IRB.CreateCall(HwasanGenerateTagFunc), IntptrTy);
408}
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000409
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000410Value *HWAddressSanitizer::getStackBaseTag(IRBuilder<> &IRB) {
411 if (ClGenerateTagsWithCalls)
412 return nullptr;
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000413 // FIXME: use addressofreturnaddress (but implement it in aarch64 backend
414 // first).
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000415 Module *M = IRB.GetInsertBlock()->getParent()->getParent();
416 auto GetStackPointerFn =
417 Intrinsic::getDeclaration(M, Intrinsic::frameaddress);
418 Value *StackPointer = IRB.CreateCall(
419 GetStackPointerFn, {Constant::getNullValue(IRB.getInt32Ty())});
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000420
421 // Extract some entropy from the stack pointer for the tags.
422 // Take bits 20..28 (ASLR entropy) and xor with bits 0..8 (these differ
423 // between functions).
424 Value *StackPointerLong = IRB.CreatePointerCast(StackPointer, IntptrTy);
425 Value *StackTag =
426 IRB.CreateXor(StackPointerLong, IRB.CreateLShr(StackPointerLong, 20),
427 "hwasan.stack.base.tag");
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000428 return StackTag;
429}
430
431Value *HWAddressSanitizer::getAllocaTag(IRBuilder<> &IRB, Value *StackTag,
432 AllocaInst *AI, unsigned AllocaNo) {
433 if (ClGenerateTagsWithCalls)
434 return getNextTagWithCall(IRB);
435 return IRB.CreateXor(StackTag,
436 ConstantInt::get(IntptrTy, RetagMask(AllocaNo)));
437}
438
439Value *HWAddressSanitizer::getUARTag(IRBuilder<> &IRB, Value *StackTag) {
440 if (ClGenerateTagsWithCalls)
441 return getNextTagWithCall(IRB);
442 return IRB.CreateXor(StackTag, ConstantInt::get(IntptrTy, 0xFFU));
443}
444
Evgeniy Stepanov80ccda22018-02-09 00:59:10 +0000445// Add a tag to an address.
446Value *HWAddressSanitizer::tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong,
447 Value *Tag) {
448 Value *TaggedPtrLong;
449 if (ClEnableKhwasan) {
450 // Kernel addresses have 0xFF in the most significant byte.
451 Value *ShiftedTag = IRB.CreateOr(
452 IRB.CreateShl(Tag, kPointerTagShift),
453 ConstantInt::get(IntptrTy, (1ULL << kPointerTagShift) - 1));
454 TaggedPtrLong = IRB.CreateAnd(PtrLong, ShiftedTag);
455 } else {
456 // Userspace can simply do OR (tag << 56);
457 Value *ShiftedTag = IRB.CreateShl(Tag, kPointerTagShift);
458 TaggedPtrLong = IRB.CreateOr(PtrLong, ShiftedTag);
459 }
460 return IRB.CreateIntToPtr(TaggedPtrLong, Ty);
461}
462
Evgeniy Stepanov43271b12018-02-21 19:52:23 +0000463// Remove tag from an address.
464Value *HWAddressSanitizer::untagPointer(IRBuilder<> &IRB, Value *PtrLong) {
465 Value *UntaggedPtrLong;
466 if (ClEnableKhwasan) {
467 // Kernel addresses have 0xFF in the most significant byte.
468 UntaggedPtrLong = IRB.CreateOr(PtrLong,
469 ConstantInt::get(PtrLong->getType(), 0xFFULL << kPointerTagShift));
470 } else {
471 // Userspace addresses have 0x00.
472 UntaggedPtrLong = IRB.CreateAnd(PtrLong,
473 ConstantInt::get(PtrLong->getType(), ~(0xFFULL << kPointerTagShift)));
474 }
475 return UntaggedPtrLong;
476}
477
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000478bool HWAddressSanitizer::instrumentStack(
479 SmallVectorImpl<AllocaInst *> &Allocas,
480 SmallVectorImpl<Instruction *> &RetVec) {
481 Function *F = Allocas[0]->getParent()->getParent();
482 Instruction *InsertPt = &*F->getEntryBlock().begin();
483 IRBuilder<> IRB(InsertPt);
484
485 Value *StackTag = getStackBaseTag(IRB);
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000486
487 // Ideally, we want to calculate tagged stack base pointer, and rewrite all
488 // alloca addresses using that. Unfortunately, offsets are not known yet
489 // (unless we use ASan-style mega-alloca). Instead we keep the base tag in a
490 // temp, shift-OR it into each alloca address and xor with the retag mask.
491 // This generates one extra instruction per alloca use.
492 for (unsigned N = 0; N < Allocas.size(); ++N) {
493 auto *AI = Allocas[N];
494 IRB.SetInsertPoint(AI->getNextNode());
495
496 // Replace uses of the alloca with tagged address.
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000497 Value *Tag = getAllocaTag(IRB, StackTag, AI, N);
498 Value *AILong = IRB.CreatePointerCast(AI, IntptrTy);
Evgeniy Stepanov80ccda22018-02-09 00:59:10 +0000499 Value *Replacement = tagPointer(IRB, AI->getType(), AILong, Tag);
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000500 std::string Name =
501 AI->hasName() ? AI->getName().str() : "alloca." + itostr(N);
Evgeniy Stepanov80ccda22018-02-09 00:59:10 +0000502 Replacement->setName(Name + ".hwasan");
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000503
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000504 for (auto UI = AI->use_begin(), UE = AI->use_end(); UI != UE;) {
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000505 Use &U = *UI++;
506 if (U.getUser() != AILong)
507 U.set(Replacement);
508 }
509
510 tagAlloca(IRB, AI, Tag);
511
512 for (auto RI : RetVec) {
513 IRB.SetInsertPoint(RI);
514
515 // Re-tag alloca memory with the special UAR tag.
Evgeniy Stepanov080e0d42018-01-13 01:32:15 +0000516 Value *Tag = getUARTag(IRB, StackTag);
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000517 tagAlloca(IRB, AI, Tag);
518 }
519 }
520
521 return true;
522}
523
524bool HWAddressSanitizer::isInterestingAlloca(const AllocaInst &AI) {
525 return (AI.getAllocatedType()->isSized() &&
526 // FIXME: instrument dynamic allocas, too
527 AI.isStaticAlloca() &&
528 // alloca() may be called with 0 size, ignore it.
529 getAllocaSizeInBytes(AI) > 0 &&
530 // We are only interested in allocas not promotable to registers.
531 // Promotable allocas are common under -O0.
532 !isAllocaPromotable(&AI) &&
533 // inalloca allocas are not treated as static, and we don't want
534 // dynamic alloca instrumentation for them as well.
535 !AI.isUsedWithInAlloca() &&
536 // swifterror allocas are register promoted by ISel
537 !AI.isSwiftError());
538}
539
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000540bool HWAddressSanitizer::runOnFunction(Function &F) {
541 if (&F == HwasanCtorFunction)
542 return false;
543
544 if (!F.hasFnAttribute(Attribute::SanitizeHWAddress))
545 return false;
546
547 DEBUG(dbgs() << "Function: " << F.getName() << "\n");
548
549 initializeCallbacks(*F.getParent());
550
551 bool Changed = false;
552 SmallVector<Instruction*, 16> ToInstrument;
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000553 SmallVector<AllocaInst*, 8> AllocasToInstrument;
554 SmallVector<Instruction*, 8> RetVec;
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000555 for (auto &BB : F) {
556 for (auto &Inst : BB) {
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000557 if (ClInstrumentStack)
558 if (AllocaInst *AI = dyn_cast<AllocaInst>(&Inst)) {
559 // Realign all allocas. We don't want small uninteresting allocas to
560 // hide in instrumented alloca's padding.
561 if (AI->getAlignment() < kAllocaAlignment)
562 AI->setAlignment(kAllocaAlignment);
563 // Instrument some of them.
564 if (isInterestingAlloca(*AI))
565 AllocasToInstrument.push_back(AI);
566 continue;
567 }
568
569 if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst) || isa<CleanupReturnInst>(Inst))
570 RetVec.push_back(&Inst);
571
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000572 Value *MaybeMask = nullptr;
573 bool IsWrite;
574 unsigned Alignment;
575 uint64_t TypeSize;
576 Value *Addr = isInterestingMemoryAccess(&Inst, &IsWrite, &TypeSize,
577 &Alignment, &MaybeMask);
578 if (Addr || isa<MemIntrinsic>(Inst))
579 ToInstrument.push_back(&Inst);
580 }
581 }
582
Evgeniy Stepanov99fa3e72018-01-11 22:53:30 +0000583 if (!AllocasToInstrument.empty())
584 Changed |= instrumentStack(AllocasToInstrument, RetVec);
585
Evgeniy Stepanovc667c1f2017-12-09 00:21:41 +0000586 for (auto Inst : ToInstrument)
587 Changed |= instrumentMemAccess(Inst);
588
589 return Changed;
590}