| //===- subzero/src/IceASanInstrumentation.cpp - ASan ------------*- C++ -*-===// | 
 | // | 
 | //                        The Subzero Code Generator | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// \brief Implements the AddressSanitizer instrumentation class. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "IceASanInstrumentation.h" | 
 |  | 
 | #include "IceBuildDefs.h" | 
 | #include "IceCfg.h" | 
 | #include "IceCfgNode.h" | 
 | #include "IceGlobalInits.h" | 
 | #include "IceInst.h" | 
 | #include "IceTargetLowering.h" | 
 | #include "IceTypes.h" | 
 |  | 
 | #include <sstream> | 
 | #include <unordered_map> | 
 | #include <unordered_set> | 
 | #include <vector> | 
 |  | 
 | namespace Ice { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr SizeT BytesPerWord = sizeof(uint32_t); | 
 | constexpr SizeT RzSize = 32; | 
 | constexpr SizeT ShadowScaleLog2 = 3; | 
 | constexpr SizeT ShadowScale = 1 << ShadowScaleLog2; | 
 | constexpr SizeT ShadowLength32 = 1 << (32 - ShadowScaleLog2); | 
 | constexpr int32_t StackPoisonVal = -1; | 
 | constexpr const char *ASanPrefix = "__asan"; | 
 | constexpr const char *RzPrefix = "__$rz"; | 
 | constexpr const char *RzArrayName = "__$rz_array"; | 
 | constexpr const char *RzSizesName = "__$rz_sizes"; | 
 | const llvm::NaClBitcodeRecord::RecordVector RzContents = | 
 |     llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R'); | 
 |  | 
 | // In order to instrument the code correctly, the .pexe must not have had its | 
 | // symbols stripped. | 
 | using StringMap = std::unordered_map<std::string, std::string>; | 
 | using StringSet = std::unordered_set<std::string>; | 
 | // TODO(tlively): Handle all allocation functions | 
 | const StringMap FuncSubstitutions = {{"malloc", "__asan_malloc"}, | 
 |                                      {"free", "__asan_free"}, | 
 |                                      {"calloc", "__asan_calloc"}, | 
 |                                      {"__asan_dummy_calloc", "__asan_calloc"}, | 
 |                                      {"realloc", "__asan_realloc"}}; | 
 | const StringSet FuncBlackList = {"_Balloc"}; | 
 |  | 
 | llvm::NaClBitcodeRecord::RecordVector sizeToByteVec(SizeT Size) { | 
 |   llvm::NaClBitcodeRecord::RecordVector SizeContents; | 
 |   for (unsigned i = 0; i < sizeof(Size); ++i) { | 
 |     SizeContents.emplace_back(Size % (1 << CHAR_BIT)); | 
 |     Size >>= CHAR_BIT; | 
 |   } | 
 |   return SizeContents; | 
 | } | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | ICE_TLS_DEFINE_FIELD(VarSizeMap *, ASanInstrumentation, LocalVars); | 
 | ICE_TLS_DEFINE_FIELD(std::vector<InstStore *> *, ASanInstrumentation, | 
 |                      LocalDtors); | 
 | ICE_TLS_DEFINE_FIELD(CfgNode *, ASanInstrumentation, CurNode); | 
 | ICE_TLS_DEFINE_FIELD(VarSizeMap *, ASanInstrumentation, CheckedVars); | 
 |  | 
 | bool ASanInstrumentation::isInstrumentable(Cfg *Func) { | 
 |   std::string FuncName = Func->getFunctionName().toStringOrEmpty(); | 
 |   return FuncName == "" || | 
 |          (FuncBlackList.count(FuncName) == 0 && FuncName.find(ASanPrefix) != 0); | 
 | } | 
 |  | 
 | // Create redzones around all global variables, ensuring that the initializer | 
 | // types of the redzones and their associated globals match so that they are | 
 | // laid out together in memory. | 
 | void ASanInstrumentation::instrumentGlobals(VariableDeclarationList &Globals) { | 
 |   std::unique_lock<std::mutex> _(GlobalsMutex); | 
 |   if (DidProcessGlobals) | 
 |     return; | 
 |   VariableDeclarationList NewGlobals; | 
 |   // Global holding pointers to all redzones | 
 |   auto *RzArray = VariableDeclaration::create(&NewGlobals); | 
 |   // Global holding sizes of all redzones | 
 |   auto *RzSizes = VariableDeclaration::create(&NewGlobals); | 
 |  | 
 |   RzArray->setName(Ctx, RzArrayName); | 
 |   RzSizes->setName(Ctx, RzSizesName); | 
 |   RzArray->setIsConstant(true); | 
 |   RzSizes->setIsConstant(true); | 
 |   NewGlobals.push_back(RzArray); | 
 |   NewGlobals.push_back(RzSizes); | 
 |  | 
 |   using PrototypeMap = std::unordered_map<std::string, FunctionDeclaration *>; | 
 |   PrototypeMap ProtoSubstitutions; | 
 |   for (VariableDeclaration *Global : Globals) { | 
 |     assert(Global->getAlignment() <= RzSize); | 
 |     VariableDeclaration *RzLeft = VariableDeclaration::create(&NewGlobals); | 
 |     VariableDeclaration *NewGlobal = Global; | 
 |     VariableDeclaration *RzRight = VariableDeclaration::create(&NewGlobals); | 
 |     RzLeft->setName(Ctx, nextRzName()); | 
 |     RzRight->setName(Ctx, nextRzName()); | 
 |     SizeT Alignment = std::max(RzSize, Global->getAlignment()); | 
 |     SizeT RzLeftSize = Alignment; | 
 |     SizeT RzRightSize = | 
 |         RzSize + Utils::OffsetToAlignment(Global->getNumBytes(), Alignment); | 
 |     if (!Global->hasNonzeroInitializer()) { | 
 |       RzLeft->addInitializer(VariableDeclaration::ZeroInitializer::create( | 
 |           &NewGlobals, RzLeftSize)); | 
 |       RzRight->addInitializer(VariableDeclaration::ZeroInitializer::create( | 
 |           &NewGlobals, RzRightSize)); | 
 |     } else { | 
 |       RzLeft->addInitializer(VariableDeclaration::DataInitializer::create( | 
 |           &NewGlobals, llvm::NaClBitcodeRecord::RecordVector(RzLeftSize, 'R'))); | 
 |       RzRight->addInitializer(VariableDeclaration::DataInitializer::create( | 
 |           &NewGlobals, | 
 |           llvm::NaClBitcodeRecord::RecordVector(RzRightSize, 'R'))); | 
 |  | 
 |       // replace any pointers to allocator functions | 
 |       NewGlobal = VariableDeclaration::create(&NewGlobals); | 
 |       NewGlobal->setName(Global->getName()); | 
 |       std::vector<VariableDeclaration::Initializer *> GlobalInits = | 
 |           Global->getInitializers(); | 
 |       for (VariableDeclaration::Initializer *Init : GlobalInits) { | 
 |         auto *RelocInit = | 
 |             llvm::dyn_cast<VariableDeclaration::RelocInitializer>(Init); | 
 |         if (RelocInit == nullptr) { | 
 |           NewGlobal->addInitializer(Init); | 
 |           continue; | 
 |         } | 
 |         const GlobalDeclaration *TargetDecl = RelocInit->getDeclaration(); | 
 |         const auto *TargetFunc = | 
 |             llvm::dyn_cast<FunctionDeclaration>(TargetDecl); | 
 |         if (TargetFunc == nullptr) { | 
 |           NewGlobal->addInitializer(Init); | 
 |           continue; | 
 |         } | 
 |         std::string TargetName = TargetDecl->getName().toStringOrEmpty(); | 
 |         StringMap::const_iterator Subst = FuncSubstitutions.find(TargetName); | 
 |         if (Subst == FuncSubstitutions.end()) { | 
 |           NewGlobal->addInitializer(Init); | 
 |           continue; | 
 |         } | 
 |         std::string SubstName = Subst->second; | 
 |         PrototypeMap::iterator SubstProtoEntry = | 
 |             ProtoSubstitutions.find(SubstName); | 
 |         FunctionDeclaration *SubstProto; | 
 |         if (SubstProtoEntry != ProtoSubstitutions.end()) | 
 |           SubstProto = SubstProtoEntry->second; | 
 |         else { | 
 |           constexpr bool IsProto = true; | 
 |           SubstProto = FunctionDeclaration::create( | 
 |               Ctx, TargetFunc->getSignature(), TargetFunc->getCallingConv(), | 
 |               llvm::GlobalValue::ExternalLinkage, IsProto); | 
 |           SubstProto->setName(Ctx, SubstName); | 
 |           ProtoSubstitutions.insert({SubstName, SubstProto}); | 
 |         } | 
 |  | 
 |         NewGlobal->addInitializer(VariableDeclaration::RelocInitializer::create( | 
 |             &NewGlobals, SubstProto, RelocOffsetArray(0))); | 
 |       } | 
 |     } | 
 |  | 
 |     RzLeft->setIsConstant(Global->getIsConstant()); | 
 |     NewGlobal->setIsConstant(Global->getIsConstant()); | 
 |     RzRight->setIsConstant(Global->getIsConstant()); | 
 |     RzLeft->setAlignment(Alignment); | 
 |     NewGlobal->setAlignment(Alignment); | 
 |     RzRight->setAlignment(1); | 
 |     RzArray->addInitializer(VariableDeclaration::RelocInitializer::create( | 
 |         &NewGlobals, RzLeft, RelocOffsetArray(0))); | 
 |     RzArray->addInitializer(VariableDeclaration::RelocInitializer::create( | 
 |         &NewGlobals, RzRight, RelocOffsetArray(0))); | 
 |     RzSizes->addInitializer(VariableDeclaration::DataInitializer::create( | 
 |         &NewGlobals, sizeToByteVec(RzLeftSize))); | 
 |     RzSizes->addInitializer(VariableDeclaration::DataInitializer::create( | 
 |         &NewGlobals, sizeToByteVec(RzRightSize))); | 
 |  | 
 |     NewGlobals.push_back(RzLeft); | 
 |     NewGlobals.push_back(NewGlobal); | 
 |     NewGlobals.push_back(RzRight); | 
 |     RzGlobalsNum += 2; | 
 |  | 
 |     GlobalSizes.insert({NewGlobal->getName(), NewGlobal->getNumBytes()}); | 
 |   } | 
 |  | 
 |   // Replace old list of globals, without messing up arena allocators | 
 |   Globals.clear(); | 
 |   Globals.merge(&NewGlobals); | 
 |   DidProcessGlobals = true; | 
 |  | 
 |   // Log the new set of globals | 
 |   if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_GlobalInit)) { | 
 |     OstreamLocker _(Ctx); | 
 |     Ctx->getStrDump() << "========= Instrumented Globals =========\n"; | 
 |     for (VariableDeclaration *Global : Globals) { | 
 |       Global->dump(Ctx->getStrDump()); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | std::string ASanInstrumentation::nextRzName() { | 
 |   std::stringstream Name; | 
 |   Name << RzPrefix << RzNum++; | 
 |   return Name.str(); | 
 | } | 
 |  | 
 | // Check for an alloca signaling the presence of local variables and add a | 
 | // redzone if it is found | 
 | void ASanInstrumentation::instrumentFuncStart(LoweringContext &Context) { | 
 |   if (ICE_TLS_GET_FIELD(LocalDtors) == nullptr) { | 
 |     ICE_TLS_SET_FIELD(LocalDtors, new std::vector<InstStore *>()); | 
 |     ICE_TLS_SET_FIELD(LocalVars, new VarSizeMap()); | 
 |   } | 
 |   Cfg *Func = Context.getNode()->getCfg(); | 
 |   using Entry = std::pair<SizeT, int32_t>; | 
 |   std::vector<InstAlloca *> NewAllocas; | 
 |   std::vector<Entry> PoisonVals; | 
 |   Variable *FirstShadowLocVar; | 
 |   InstArithmetic *ShadowIndexCalc; | 
 |   InstArithmetic *ShadowLocCalc; | 
 |   InstAlloca *Cur; | 
 |   ConstantInteger32 *VarSizeOp; | 
 |   while (!Context.atEnd()) { | 
 |     Cur = llvm::dyn_cast<InstAlloca>(iteratorToInst(Context.getCur())); | 
 |     VarSizeOp = (Cur == nullptr) | 
 |                     ? nullptr | 
 |                     : llvm::dyn_cast<ConstantInteger32>(Cur->getSizeInBytes()); | 
 |     if (Cur == nullptr || VarSizeOp == nullptr) { | 
 |       Context.advanceCur(); | 
 |       Context.advanceNext(); | 
 |       continue; | 
 |     } | 
 |  | 
 |     Cur->setDeleted(); | 
 |  | 
 |     if (PoisonVals.empty()) { | 
 |       // insert leftmost redzone | 
 |       auto *LastRzVar = Func->makeVariable(IceType_i32); | 
 |       LastRzVar->setName(Func, nextRzName()); | 
 |       auto *ByteCount = ConstantInteger32::create(Ctx, IceType_i32, RzSize); | 
 |       constexpr SizeT Alignment = 8; | 
 |       NewAllocas.emplace_back( | 
 |           InstAlloca::create(Func, LastRzVar, ByteCount, Alignment)); | 
 |       PoisonVals.emplace_back(Entry{RzSize >> ShadowScaleLog2, StackPoisonVal}); | 
 |  | 
 |       // Calculate starting address for poisoning | 
 |       FirstShadowLocVar = Func->makeVariable(IceType_i32); | 
 |       FirstShadowLocVar->setName(Func, "firstShadowLoc"); | 
 |       auto *ShadowIndexVar = Func->makeVariable(IceType_i32); | 
 |       ShadowIndexVar->setName(Func, "shadowIndex"); | 
 |  | 
 |       auto *ShadowScaleLog2Const = | 
 |           ConstantInteger32::create(Ctx, IceType_i32, ShadowScaleLog2); | 
 |       auto *ShadowMemLocConst = | 
 |           ConstantInteger32::create(Ctx, IceType_i32, ShadowLength32); | 
 |  | 
 |       ShadowIndexCalc = | 
 |           InstArithmetic::create(Func, InstArithmetic::Lshr, ShadowIndexVar, | 
 |                                  LastRzVar, ShadowScaleLog2Const); | 
 |       ShadowLocCalc = | 
 |           InstArithmetic::create(Func, InstArithmetic::Add, FirstShadowLocVar, | 
 |                                  ShadowIndexVar, ShadowMemLocConst); | 
 |     } | 
 |  | 
 |     // create the new alloca that includes a redzone | 
 |     SizeT VarSize = VarSizeOp->getValue(); | 
 |     Variable *Dest = Cur->getDest(); | 
 |     ICE_TLS_GET_FIELD(LocalVars)->insert({Dest, VarSize}); | 
 |     SizeT RzPadding = RzSize + Utils::OffsetToAlignment(VarSize, RzSize); | 
 |     auto *ByteCount = | 
 |         ConstantInteger32::create(Ctx, IceType_i32, VarSize + RzPadding); | 
 |     constexpr SizeT Alignment = 8; | 
 |     NewAllocas.emplace_back( | 
 |         InstAlloca::create(Func, Dest, ByteCount, Alignment)); | 
 |  | 
 |     const SizeT Zeros = VarSize >> ShadowScaleLog2; | 
 |     const SizeT Offset = VarSize % ShadowScale; | 
 |     const SizeT PoisonBytes = | 
 |         ((VarSize + RzPadding) >> ShadowScaleLog2) - Zeros - 1; | 
 |     if (Zeros > 0) | 
 |       PoisonVals.emplace_back(Entry{Zeros, 0}); | 
 |     PoisonVals.emplace_back(Entry{1, (Offset == 0) ? StackPoisonVal : Offset}); | 
 |     PoisonVals.emplace_back(Entry{PoisonBytes, StackPoisonVal}); | 
 |     Context.advanceCur(); | 
 |     Context.advanceNext(); | 
 |   } | 
 |  | 
 |   Context.rewind(); | 
 |   if (PoisonVals.empty()) { | 
 |     Context.advanceNext(); | 
 |     return; | 
 |   } | 
 |   for (InstAlloca *RzAlloca : NewAllocas) { | 
 |     Context.insert(RzAlloca); | 
 |   } | 
 |   Context.insert(ShadowIndexCalc); | 
 |   Context.insert(ShadowLocCalc); | 
 |  | 
 |   // Poison redzones | 
 |   std::vector<Entry>::iterator Iter = PoisonVals.begin(); | 
 |   for (SizeT Offset = 0; Iter != PoisonVals.end(); Offset += BytesPerWord) { | 
 |     int32_t CurVals[BytesPerWord] = {0}; | 
 |     for (uint32_t i = 0; i < BytesPerWord; ++i) { | 
 |       if (Iter == PoisonVals.end()) | 
 |         break; | 
 |       Entry Val = *Iter; | 
 |       CurVals[i] = Val.second; | 
 |       --Val.first; | 
 |       if (Val.first > 0) | 
 |         *Iter = Val; | 
 |       else | 
 |         ++Iter; | 
 |     } | 
 |     int32_t Poison = ((CurVals[3] & 0xff) << 24) | ((CurVals[2] & 0xff) << 16) | | 
 |                      ((CurVals[1] & 0xff) << 8) | (CurVals[0] & 0xff); | 
 |     if (Poison == 0) | 
 |       continue; | 
 |     auto *PoisonConst = ConstantInteger32::create(Ctx, IceType_i32, Poison); | 
 |     auto *ZeroConst = ConstantInteger32::create(Ctx, IceType_i32, 0); | 
 |     auto *OffsetConst = ConstantInteger32::create(Ctx, IceType_i32, Offset); | 
 |     auto *PoisonAddrVar = Func->makeVariable(IceType_i32); | 
 |     Context.insert(InstArithmetic::create(Func, InstArithmetic::Add, | 
 |                                           PoisonAddrVar, FirstShadowLocVar, | 
 |                                           OffsetConst)); | 
 |     Context.insert(InstStore::create(Func, PoisonConst, PoisonAddrVar)); | 
 |     ICE_TLS_GET_FIELD(LocalDtors) | 
 |         ->emplace_back(InstStore::create(Func, ZeroConst, PoisonAddrVar)); | 
 |   } | 
 |   Context.advanceNext(); | 
 | } | 
 |  | 
 | void ASanInstrumentation::instrumentCall(LoweringContext &Context, | 
 |                                          InstCall *Instr) { | 
 |   auto *CallTarget = | 
 |       llvm::dyn_cast<ConstantRelocatable>(Instr->getCallTarget()); | 
 |   if (CallTarget == nullptr) | 
 |     return; | 
 |  | 
 |   std::string TargetName = CallTarget->getName().toStringOrEmpty(); | 
 |   auto Subst = FuncSubstitutions.find(TargetName); | 
 |   if (Subst == FuncSubstitutions.end()) | 
 |     return; | 
 |  | 
 |   std::string SubName = Subst->second; | 
 |   Constant *NewFunc = Ctx->getConstantExternSym(Ctx->getGlobalString(SubName)); | 
 |   auto *NewCall = | 
 |       InstCall::create(Context.getNode()->getCfg(), Instr->getNumArgs(), | 
 |                        Instr->getDest(), NewFunc, Instr->isTailcall()); | 
 |   for (SizeT I = 0, Args = Instr->getNumArgs(); I < Args; ++I) | 
 |     NewCall->addArg(Instr->getArg(I)); | 
 |   Context.insert(NewCall); | 
 |   Instr->setDeleted(); | 
 | } | 
 |  | 
 | void ASanInstrumentation::instrumentLoad(LoweringContext &Context, | 
 |                                          InstLoad *Instr) { | 
 |   Operand *Src = Instr->getSourceAddress(); | 
 |   if (auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Src)) { | 
 |     auto *NewLoad = InstLoad::create(Context.getNode()->getCfg(), | 
 |                                      Instr->getDest(), instrumentReloc(Reloc)); | 
 |     Instr->setDeleted(); | 
 |     Context.insert(NewLoad); | 
 |     Instr = NewLoad; | 
 |   } | 
 |   Constant *Func = | 
 |       Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_check_load")); | 
 |   instrumentAccess(Context, Instr->getSourceAddress(), | 
 |                    typeWidthInBytes(Instr->getDest()->getType()), Func); | 
 | } | 
 |  | 
 | void ASanInstrumentation::instrumentStore(LoweringContext &Context, | 
 |                                           InstStore *Instr) { | 
 |   Operand *Data = Instr->getData(); | 
 |   if (auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Data)) { | 
 |     auto *NewStore = InstStore::create( | 
 |         Context.getNode()->getCfg(), instrumentReloc(Reloc), Instr->getAddr()); | 
 |     Instr->setDeleted(); | 
 |     Context.insert(NewStore); | 
 |     Instr = NewStore; | 
 |   } | 
 |   Constant *Func = | 
 |       Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_check_store")); | 
 |   instrumentAccess(Context, Instr->getAddr(), | 
 |                    typeWidthInBytes(Instr->getData()->getType()), Func); | 
 | } | 
 |  | 
 | ConstantRelocatable * | 
 | ASanInstrumentation::instrumentReloc(ConstantRelocatable *Reloc) { | 
 |   std::string DataName = Reloc->getName().toString(); | 
 |   StringMap::const_iterator DataSub = FuncSubstitutions.find(DataName); | 
 |   if (DataSub != FuncSubstitutions.end()) { | 
 |     return ConstantRelocatable::create( | 
 |         Ctx, Reloc->getType(), | 
 |         RelocatableTuple(Reloc->getOffset(), RelocOffsetArray(0), | 
 |                          Ctx->getGlobalString(DataSub->second), | 
 |                          Reloc->getEmitString())); | 
 |   } | 
 |   return Reloc; | 
 | } | 
 |  | 
 | void ASanInstrumentation::instrumentAccess(LoweringContext &Context, | 
 |                                            Operand *Op, SizeT Size, | 
 |                                            Constant *CheckFunc) { | 
 |   // Skip redundant checks within basic blocks | 
 |   VarSizeMap *Checked = ICE_TLS_GET_FIELD(CheckedVars); | 
 |   if (ICE_TLS_GET_FIELD(CurNode) != Context.getNode()) { | 
 |     ICE_TLS_SET_FIELD(CurNode, Context.getNode()); | 
 |     if (Checked == NULL) { | 
 |       Checked = new VarSizeMap(); | 
 |       ICE_TLS_SET_FIELD(CheckedVars, Checked); | 
 |     } | 
 |     Checked->clear(); | 
 |   } | 
 |   VarSizeMap::iterator PrevCheck = Checked->find(Op); | 
 |   if (PrevCheck != Checked->end() && PrevCheck->second >= Size) | 
 |     return; | 
 |   else | 
 |     Checked->insert({Op, Size}); | 
 |  | 
 |   // check for known good local access | 
 |   VarSizeMap::iterator LocalSize = ICE_TLS_GET_FIELD(LocalVars)->find(Op); | 
 |   if (LocalSize != ICE_TLS_GET_FIELD(LocalVars)->end() && | 
 |       LocalSize->second >= Size) | 
 |     return; | 
 |   if (isOkGlobalAccess(Op, Size)) | 
 |     return; | 
 |   constexpr SizeT NumArgs = 2; | 
 |   constexpr Variable *Void = nullptr; | 
 |   constexpr bool NoTailCall = false; | 
 |   auto *Call = InstCall::create(Context.getNode()->getCfg(), NumArgs, Void, | 
 |                                 CheckFunc, NoTailCall); | 
 |   Call->addArg(Op); | 
 |   Call->addArg(ConstantInteger32::create(Ctx, IceType_i32, Size)); | 
 |   // play games to insert the call before the access instruction | 
 |   InstList::iterator Next = Context.getNext(); | 
 |   Context.setInsertPoint(Context.getCur()); | 
 |   Context.insert(Call); | 
 |   Context.setNext(Next); | 
 | } | 
 |  | 
 | // TODO(tlively): Trace back load and store addresses to find their real offsets | 
 | bool ASanInstrumentation::isOkGlobalAccess(Operand *Op, SizeT Size) { | 
 |   auto *Reloc = llvm::dyn_cast<ConstantRelocatable>(Op); | 
 |   if (Reloc == nullptr) | 
 |     return false; | 
 |   RelocOffsetT Offset = Reloc->getOffset(); | 
 |   GlobalSizeMap::iterator GlobalSize = GlobalSizes.find(Reloc->getName()); | 
 |   return GlobalSize != GlobalSizes.end() && GlobalSize->second - Offset >= Size; | 
 | } | 
 |  | 
 | void ASanInstrumentation::instrumentRet(LoweringContext &Context, InstRet *) { | 
 |   Cfg *Func = Context.getNode()->getCfg(); | 
 |   Context.setInsertPoint(Context.getCur()); | 
 |   for (InstStore *RzUnpoison : *ICE_TLS_GET_FIELD(LocalDtors)) { | 
 |     Context.insert( | 
 |         InstStore::create(Func, RzUnpoison->getData(), RzUnpoison->getAddr())); | 
 |   } | 
 |   Context.advanceCur(); | 
 |   Context.advanceNext(); | 
 | } | 
 |  | 
 | void ASanInstrumentation::instrumentStart(Cfg *Func) { | 
 |   Constant *ShadowMemInit = | 
 |       Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_init")); | 
 |   constexpr SizeT NumArgs = 3; | 
 |   constexpr Variable *Void = nullptr; | 
 |   constexpr bool NoTailCall = false; | 
 |   auto *Call = InstCall::create(Func, NumArgs, Void, ShadowMemInit, NoTailCall); | 
 |   Func->getEntryNode()->getInsts().push_front(Call); | 
 |  | 
 |   instrumentGlobals(*getGlobals()); | 
 |  | 
 |   Call->addArg(ConstantInteger32::create(Ctx, IceType_i32, RzGlobalsNum)); | 
 |   Call->addArg(Ctx->getConstantSym(0, Ctx->getGlobalString(RzArrayName))); | 
 |   Call->addArg(Ctx->getConstantSym(0, Ctx->getGlobalString(RzSizesName))); | 
 | } | 
 |  | 
 | // TODO(tlively): make this more efficient with swap idiom | 
 | void ASanInstrumentation::finishFunc(Cfg *) { | 
 |   ICE_TLS_GET_FIELD(LocalVars)->clear(); | 
 |   ICE_TLS_GET_FIELD(LocalDtors)->clear(); | 
 | } | 
 |  | 
 | } // end of namespace Ice |