| //===- MemRegion.cpp - Abstract memory regions for static analysis --------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines MemRegion and its subclasses. MemRegion defines a |
| // partially-typed abstraction of memory useful for path-sensitive dataflow |
| // analyses. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Attr.h" |
| #include "clang/AST/CharUnits.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/PrettyPrinter.h" |
| #include "clang/AST/RecordLayout.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Analysis/AnalysisDeclContext.h" |
| #include "clang/Analysis/Support/BumpVector.h" |
| #include "clang/Basic/IdentifierTable.h" |
| #include "clang/Basic/LLVM.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/FoldingSet.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/PointerUnion.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/CheckedArithmetic.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cassert> |
| #include <cstdint> |
| #include <functional> |
| #include <iterator> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| using namespace clang; |
| using namespace ento; |
| |
| #define DEBUG_TYPE "MemRegion" |
| |
| //===----------------------------------------------------------------------===// |
| // MemRegion Construction. |
| //===----------------------------------------------------------------------===// |
| |
| template <typename RegionTy, typename SuperTy, typename Arg1Ty> |
| RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, |
| const SuperTy *superRegion) { |
| llvm::FoldingSetNodeID ID; |
| RegionTy::ProfileRegion(ID, arg1, superRegion); |
| void *InsertPos; |
| auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); |
| |
| if (!R) { |
| R = A.Allocate<RegionTy>(); |
| new (R) RegionTy(arg1, superRegion); |
| Regions.InsertNode(R, InsertPos); |
| } |
| |
| return R; |
| } |
| |
| template <typename RegionTy, typename SuperTy, typename Arg1Ty, typename Arg2Ty> |
| RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, |
| const SuperTy *superRegion) { |
| llvm::FoldingSetNodeID ID; |
| RegionTy::ProfileRegion(ID, arg1, arg2, superRegion); |
| void *InsertPos; |
| auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); |
| |
| if (!R) { |
| R = A.Allocate<RegionTy>(); |
| new (R) RegionTy(arg1, arg2, superRegion); |
| Regions.InsertNode(R, InsertPos); |
| } |
| |
| return R; |
| } |
| |
| template <typename RegionTy, typename SuperTy, |
| typename Arg1Ty, typename Arg2Ty, typename Arg3Ty> |
| RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, |
| const Arg3Ty arg3, |
| const SuperTy *superRegion) { |
| llvm::FoldingSetNodeID ID; |
| RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion); |
| void *InsertPos; |
| auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); |
| |
| if (!R) { |
| R = A.Allocate<RegionTy>(); |
| new (R) RegionTy(arg1, arg2, arg3, superRegion); |
| Regions.InsertNode(R, InsertPos); |
| } |
| |
| return R; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Object destruction. |
| //===----------------------------------------------------------------------===// |
| |
| MemRegion::~MemRegion() = default; |
| |
| // All regions and their data are BumpPtrAllocated. No need to call their |
| // destructors. |
| MemRegionManager::~MemRegionManager() = default; |
| |
| //===----------------------------------------------------------------------===// |
| // Basic methods. |
| //===----------------------------------------------------------------------===// |
| |
| bool SubRegion::isSubRegionOf(const MemRegion* R) const { |
| const MemRegion* r = this; |
| do { |
| if (r == R) |
| return true; |
| if (const auto *sr = dyn_cast<SubRegion>(r)) |
| r = sr->getSuperRegion(); |
| else |
| break; |
| } while (r != nullptr); |
| return false; |
| } |
| |
| MemRegionManager* SubRegion::getMemRegionManager() const { |
| const SubRegion* r = this; |
| do { |
| const MemRegion *superRegion = r->getSuperRegion(); |
| if (const auto *sr = dyn_cast<SubRegion>(superRegion)) { |
| r = sr; |
| continue; |
| } |
| return superRegion->getMemRegionManager(); |
| } while (true); |
| } |
| |
| const StackFrameContext *VarRegion::getStackFrame() const { |
| const auto *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace()); |
| return SSR ? SSR->getStackFrame() : nullptr; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Region extents. |
| //===----------------------------------------------------------------------===// |
| |
| DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const { |
| ASTContext &Ctx = svalBuilder.getContext(); |
| QualType T = getDesugaredValueType(Ctx); |
| |
| if (isa<VariableArrayType>(T)) |
| return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); |
| if (T->isIncompleteType()) |
| return UnknownVal(); |
| |
| CharUnits size = Ctx.getTypeSizeInChars(T); |
| QualType sizeTy = svalBuilder.getArrayIndexType(); |
| return svalBuilder.makeIntVal(size.getQuantity(), sizeTy); |
| } |
| |
| DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { |
| // Force callers to deal with bitfields explicitly. |
| if (getDecl()->isBitField()) |
| return UnknownVal(); |
| |
| DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder); |
| |
| // A zero-length array at the end of a struct often stands for dynamically- |
| // allocated extra memory. |
| if (Extent.isZeroConstant()) { |
| QualType T = getDesugaredValueType(svalBuilder.getContext()); |
| |
| if (isa<ConstantArrayType>(T)) |
| return UnknownVal(); |
| } |
| |
| return Extent; |
| } |
| |
| DefinedOrUnknownSVal AllocaRegion::getExtent(SValBuilder &svalBuilder) const { |
| return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); |
| } |
| |
| DefinedOrUnknownSVal SymbolicRegion::getExtent(SValBuilder &svalBuilder) const { |
| return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); |
| } |
| |
| DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const { |
| return svalBuilder.makeIntVal(getStringLiteral()->getByteLength()+1, |
| svalBuilder.getArrayIndexType()); |
| } |
| |
| ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg) |
| : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} |
| |
| const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { |
| return cast<ObjCIvarDecl>(D); |
| } |
| |
| QualType ObjCIvarRegion::getValueType() const { |
| return getDecl()->getType(); |
| } |
| |
| QualType CXXBaseObjectRegion::getValueType() const { |
| return QualType(getDecl()->getTypeForDecl(), 0); |
| } |
| |
| QualType CXXDerivedObjectRegion::getValueType() const { |
| return QualType(getDecl()->getTypeForDecl(), 0); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // FoldingSet profiling. |
| //===----------------------------------------------------------------------===// |
| |
| void MemSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| ID.AddInteger(static_cast<unsigned>(getKind())); |
| } |
| |
| void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| ID.AddInteger(static_cast<unsigned>(getKind())); |
| ID.AddPointer(getStackFrame()); |
| } |
| |
| void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| ID.AddInteger(static_cast<unsigned>(getKind())); |
| ID.AddPointer(getCodeRegion()); |
| } |
| |
| void StringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, |
| const StringLiteral *Str, |
| const MemRegion *superRegion) { |
| ID.AddInteger(static_cast<unsigned>(StringRegionKind)); |
| ID.AddPointer(Str); |
| ID.AddPointer(superRegion); |
| } |
| |
| void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, |
| const ObjCStringLiteral *Str, |
| const MemRegion *superRegion) { |
| ID.AddInteger(static_cast<unsigned>(ObjCStringRegionKind)); |
| ID.AddPointer(Str); |
| ID.AddPointer(superRegion); |
| } |
| |
| void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| const Expr *Ex, unsigned cnt, |
| const MemRegion *superRegion) { |
| ID.AddInteger(static_cast<unsigned>(AllocaRegionKind)); |
| ID.AddPointer(Ex); |
| ID.AddInteger(cnt); |
| ID.AddPointer(superRegion); |
| } |
| |
| void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| ProfileRegion(ID, Ex, Cnt, superRegion); |
| } |
| |
| void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); |
| } |
| |
| void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| const CompoundLiteralExpr *CL, |
| const MemRegion* superRegion) { |
| ID.AddInteger(static_cast<unsigned>(CompoundLiteralRegionKind)); |
| ID.AddPointer(CL); |
| ID.AddPointer(superRegion); |
| } |
| |
| void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, |
| const PointerType *PT, |
| const MemRegion *sRegion) { |
| ID.AddInteger(static_cast<unsigned>(CXXThisRegionKind)); |
| ID.AddPointer(PT); |
| ID.AddPointer(sRegion); |
| } |
| |
| void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); |
| } |
| |
| void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| const ObjCIvarDecl *ivd, |
| const MemRegion* superRegion) { |
| DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind); |
| } |
| |
| void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, |
| const MemRegion* superRegion, Kind k) { |
| ID.AddInteger(static_cast<unsigned>(k)); |
| ID.AddPointer(D); |
| ID.AddPointer(superRegion); |
| } |
| |
| void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| DeclRegion::ProfileRegion(ID, D, superRegion, getKind()); |
| } |
| |
| void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| VarRegion::ProfileRegion(ID, getDecl(), superRegion); |
| } |
| |
| void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, |
| const MemRegion *sreg) { |
| ID.AddInteger(static_cast<unsigned>(MemRegion::SymbolicRegionKind)); |
| ID.Add(sym); |
| ID.AddPointer(sreg); |
| } |
| |
| void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion()); |
| } |
| |
| void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| QualType ElementType, SVal Idx, |
| const MemRegion* superRegion) { |
| ID.AddInteger(MemRegion::ElementRegionKind); |
| ID.Add(ElementType); |
| ID.AddPointer(superRegion); |
| Idx.Profile(ID); |
| } |
| |
| void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); |
| } |
| |
| void FunctionCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| const NamedDecl *FD, |
| const MemRegion*) { |
| ID.AddInteger(MemRegion::FunctionCodeRegionKind); |
| ID.AddPointer(FD); |
| } |
| |
| void FunctionCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| FunctionCodeRegion::ProfileRegion(ID, FD, superRegion); |
| } |
| |
| void BlockCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| const BlockDecl *BD, CanQualType, |
| const AnalysisDeclContext *AC, |
| const MemRegion*) { |
| ID.AddInteger(MemRegion::BlockCodeRegionKind); |
| ID.AddPointer(BD); |
| } |
| |
| void BlockCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| BlockCodeRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); |
| } |
| |
| void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, |
| const BlockCodeRegion *BC, |
| const LocationContext *LC, |
| unsigned BlkCount, |
| const MemRegion *sReg) { |
| ID.AddInteger(MemRegion::BlockDataRegionKind); |
| ID.AddPointer(BC); |
| ID.AddPointer(LC); |
| ID.AddInteger(BlkCount); |
| ID.AddPointer(sReg); |
| } |
| |
| void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { |
| BlockDataRegion::ProfileRegion(ID, BC, LC, BlockCount, getSuperRegion()); |
| } |
| |
| void CXXTempObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, |
| Expr const *Ex, |
| const MemRegion *sReg) { |
| ID.AddPointer(Ex); |
| ID.AddPointer(sReg); |
| } |
| |
| void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| ProfileRegion(ID, Ex, getSuperRegion()); |
| } |
| |
| void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, |
| const CXXRecordDecl *RD, |
| bool IsVirtual, |
| const MemRegion *SReg) { |
| ID.AddPointer(RD); |
| ID.AddBoolean(IsVirtual); |
| ID.AddPointer(SReg); |
| } |
| |
| void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| ProfileRegion(ID, getDecl(), isVirtual(), superRegion); |
| } |
| |
| void CXXDerivedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, |
| const CXXRecordDecl *RD, |
| const MemRegion *SReg) { |
| ID.AddPointer(RD); |
| ID.AddPointer(SReg); |
| } |
| |
| void CXXDerivedObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { |
| ProfileRegion(ID, getDecl(), superRegion); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Region anchors. |
| //===----------------------------------------------------------------------===// |
| |
| void GlobalsSpaceRegion::anchor() {} |
| |
| void NonStaticGlobalSpaceRegion::anchor() {} |
| |
| void StackSpaceRegion::anchor() {} |
| |
| void TypedRegion::anchor() {} |
| |
| void TypedValueRegion::anchor() {} |
| |
| void CodeTextRegion::anchor() {} |
| |
| void SubRegion::anchor() {} |
| |
| //===----------------------------------------------------------------------===// |
| // Region pretty-printing. |
| //===----------------------------------------------------------------------===// |
| |
| LLVM_DUMP_METHOD void MemRegion::dump() const { |
| dumpToStream(llvm::errs()); |
| } |
| |
| std::string MemRegion::getString() const { |
| std::string s; |
| llvm::raw_string_ostream os(s); |
| dumpToStream(os); |
| return os.str(); |
| } |
| |
| void MemRegion::dumpToStream(raw_ostream &os) const { |
| os << "<Unknown Region>"; |
| } |
| |
| void AllocaRegion::dumpToStream(raw_ostream &os) const { |
| os << "alloca{S" << Ex->getID(getContext()) << ',' << Cnt << '}'; |
| } |
| |
| void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { |
| os << "code{" << getDecl()->getDeclName().getAsString() << '}'; |
| } |
| |
| void BlockCodeRegion::dumpToStream(raw_ostream &os) const { |
| os << "block_code{" << static_cast<const void *>(this) << '}'; |
| } |
| |
| void BlockDataRegion::dumpToStream(raw_ostream &os) const { |
| os << "block_data{" << BC; |
| os << "; "; |
| for (BlockDataRegion::referenced_vars_iterator |
| I = referenced_vars_begin(), |
| E = referenced_vars_end(); I != E; ++I) |
| os << "(" << I.getCapturedRegion() << "<-" << |
| I.getOriginalRegion() << ") "; |
| os << '}'; |
| } |
| |
| void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const { |
| // FIXME: More elaborate pretty-printing. |
| os << "{ S" << CL->getID(getContext()) << " }"; |
| } |
| |
| void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { |
| os << "temp_object{" << getValueType().getAsString() << ", " |
| << "S" << Ex->getID(getContext()) << '}'; |
| } |
| |
| void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { |
| os << "Base{" << superRegion << ',' << getDecl()->getName() << '}'; |
| } |
| |
| void CXXDerivedObjectRegion::dumpToStream(raw_ostream &os) const { |
| os << "Derived{" << superRegion << ',' << getDecl()->getName() << '}'; |
| } |
| |
| void CXXThisRegion::dumpToStream(raw_ostream &os) const { |
| os << "this"; |
| } |
| |
| void ElementRegion::dumpToStream(raw_ostream &os) const { |
| os << "Element{" << superRegion << ',' |
| << Index << ',' << getElementType().getAsString() << '}'; |
| } |
| |
| void FieldRegion::dumpToStream(raw_ostream &os) const { |
| os << superRegion << "." << *getDecl(); |
| } |
| |
| void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { |
| os << "Ivar{" << superRegion << ',' << *getDecl() << '}'; |
| } |
| |
| void StringRegion::dumpToStream(raw_ostream &os) const { |
| assert(Str != nullptr && "Expecting non-null StringLiteral"); |
| Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts())); |
| } |
| |
| void ObjCStringRegion::dumpToStream(raw_ostream &os) const { |
| assert(Str != nullptr && "Expecting non-null ObjCStringLiteral"); |
| Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts())); |
| } |
| |
| void SymbolicRegion::dumpToStream(raw_ostream &os) const { |
| if (isa<HeapSpaceRegion>(getSuperRegion())) |
| os << "Heap"; |
| os << "SymRegion{" << sym << '}'; |
| } |
| |
| void VarRegion::dumpToStream(raw_ostream &os) const { |
| const auto *VD = cast<VarDecl>(D); |
| if (const IdentifierInfo *ID = VD->getIdentifier()) |
| os << ID->getName(); |
| else |
| os << "VarRegion{D" << VD->getID() << '}'; |
| } |
| |
| LLVM_DUMP_METHOD void RegionRawOffset::dump() const { |
| dumpToStream(llvm::errs()); |
| } |
| |
| void RegionRawOffset::dumpToStream(raw_ostream &os) const { |
| os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}'; |
| } |
| |
| void CodeSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "CodeSpaceRegion"; |
| } |
| |
| void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "StaticGlobalsMemSpace{" << CR << '}'; |
| } |
| |
| void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "GlobalInternalSpaceRegion"; |
| } |
| |
| void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "GlobalSystemSpaceRegion"; |
| } |
| |
| void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "GlobalImmutableSpaceRegion"; |
| } |
| |
| void HeapSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "HeapSpaceRegion"; |
| } |
| |
| void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "UnknownSpaceRegion"; |
| } |
| |
| void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "StackArgumentsSpaceRegion"; |
| } |
| |
| void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const { |
| os << "StackLocalsSpaceRegion"; |
| } |
| |
| bool MemRegion::canPrintPretty() const { |
| return canPrintPrettyAsExpr(); |
| } |
| |
| bool MemRegion::canPrintPrettyAsExpr() const { |
| return false; |
| } |
| |
| void MemRegion::printPretty(raw_ostream &os) const { |
| assert(canPrintPretty() && "This region cannot be printed pretty."); |
| os << "'"; |
| printPrettyAsExpr(os); |
| os << "'"; |
| } |
| |
| void MemRegion::printPrettyAsExpr(raw_ostream &) const { |
| llvm_unreachable("This region cannot be printed pretty."); |
| } |
| |
| bool VarRegion::canPrintPrettyAsExpr() const { |
| return true; |
| } |
| |
| void VarRegion::printPrettyAsExpr(raw_ostream &os) const { |
| os << getDecl()->getName(); |
| } |
| |
| bool ObjCIvarRegion::canPrintPrettyAsExpr() const { |
| return true; |
| } |
| |
| void ObjCIvarRegion::printPrettyAsExpr(raw_ostream &os) const { |
| os << getDecl()->getName(); |
| } |
| |
| bool FieldRegion::canPrintPretty() const { |
| return true; |
| } |
| |
| bool FieldRegion::canPrintPrettyAsExpr() const { |
| return superRegion->canPrintPrettyAsExpr(); |
| } |
| |
| void FieldRegion::printPrettyAsExpr(raw_ostream &os) const { |
| assert(canPrintPrettyAsExpr()); |
| superRegion->printPrettyAsExpr(os); |
| os << "." << getDecl()->getName(); |
| } |
| |
| void FieldRegion::printPretty(raw_ostream &os) const { |
| if (canPrintPrettyAsExpr()) { |
| os << "\'"; |
| printPrettyAsExpr(os); |
| os << "'"; |
| } else { |
| os << "field " << "\'" << getDecl()->getName() << "'"; |
| } |
| } |
| |
| bool CXXBaseObjectRegion::canPrintPrettyAsExpr() const { |
| return superRegion->canPrintPrettyAsExpr(); |
| } |
| |
| void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const { |
| superRegion->printPrettyAsExpr(os); |
| } |
| |
| bool CXXDerivedObjectRegion::canPrintPrettyAsExpr() const { |
| return superRegion->canPrintPrettyAsExpr(); |
| } |
| |
| void CXXDerivedObjectRegion::printPrettyAsExpr(raw_ostream &os) const { |
| superRegion->printPrettyAsExpr(os); |
| } |
| |
| std::string MemRegion::getDescriptiveName(bool UseQuotes) const { |
| std::string VariableName; |
| std::string ArrayIndices; |
| const MemRegion *R = this; |
| SmallString<50> buf; |
| llvm::raw_svector_ostream os(buf); |
| |
| // Obtain array indices to add them to the variable name. |
| const ElementRegion *ER = nullptr; |
| while ((ER = R->getAs<ElementRegion>())) { |
| // Index is a ConcreteInt. |
| if (auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>()) { |
| llvm::SmallString<2> Idx; |
| CI->getValue().toString(Idx); |
| ArrayIndices = (llvm::Twine("[") + Idx.str() + "]" + ArrayIndices).str(); |
| } |
| // If not a ConcreteInt, try to obtain the variable |
| // name by calling 'getDescriptiveName' recursively. |
| else { |
| std::string Idx = ER->getDescriptiveName(false); |
| if (!Idx.empty()) { |
| ArrayIndices = (llvm::Twine("[") + Idx + "]" + ArrayIndices).str(); |
| } |
| } |
| R = ER->getSuperRegion(); |
| } |
| |
| // Get variable name. |
| if (R && R->canPrintPrettyAsExpr()) { |
| R->printPrettyAsExpr(os); |
| if (UseQuotes) |
| return (llvm::Twine("'") + os.str() + ArrayIndices + "'").str(); |
| else |
| return (llvm::Twine(os.str()) + ArrayIndices).str(); |
| } |
| |
| return VariableName; |
| } |
| |
| SourceRange MemRegion::sourceRange() const { |
| const auto *const VR = dyn_cast<VarRegion>(this->getBaseRegion()); |
| const auto *const FR = dyn_cast<FieldRegion>(this); |
| |
| // Check for more specific regions first. |
| // FieldRegion |
| if (FR) { |
| return FR->getDecl()->getSourceRange(); |
| } |
| // VarRegion |
| else if (VR) { |
| return VR->getDecl()->getSourceRange(); |
| } |
| // Return invalid source range (can be checked by client). |
| else |
| return {}; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // MemRegionManager methods. |
| //===----------------------------------------------------------------------===// |
| |
| template <typename REG> |
| const REG *MemRegionManager::LazyAllocate(REG*& region) { |
| if (!region) { |
| region = A.Allocate<REG>(); |
| new (region) REG(this); |
| } |
| |
| return region; |
| } |
| |
| template <typename REG, typename ARG> |
| const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { |
| if (!region) { |
| region = A.Allocate<REG>(); |
| new (region) REG(this, a); |
| } |
| |
| return region; |
| } |
| |
| const StackLocalsSpaceRegion* |
| MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { |
| assert(STC); |
| StackLocalsSpaceRegion *&R = StackLocalsSpaceRegions[STC]; |
| |
| if (R) |
| return R; |
| |
| R = A.Allocate<StackLocalsSpaceRegion>(); |
| new (R) StackLocalsSpaceRegion(this, STC); |
| return R; |
| } |
| |
| const StackArgumentsSpaceRegion * |
| MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { |
| assert(STC); |
| StackArgumentsSpaceRegion *&R = StackArgumentsSpaceRegions[STC]; |
| |
| if (R) |
| return R; |
| |
| R = A.Allocate<StackArgumentsSpaceRegion>(); |
| new (R) StackArgumentsSpaceRegion(this, STC); |
| return R; |
| } |
| |
| const GlobalsSpaceRegion |
| *MemRegionManager::getGlobalsRegion(MemRegion::Kind K, |
| const CodeTextRegion *CR) { |
| if (!CR) { |
| if (K == MemRegion::GlobalSystemSpaceRegionKind) |
| return LazyAllocate(SystemGlobals); |
| if (K == MemRegion::GlobalImmutableSpaceRegionKind) |
| return LazyAllocate(ImmutableGlobals); |
| assert(K == MemRegion::GlobalInternalSpaceRegionKind); |
| return LazyAllocate(InternalGlobals); |
| } |
| |
| assert(K == MemRegion::StaticGlobalSpaceRegionKind); |
| StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR]; |
| if (R) |
| return R; |
| |
| R = A.Allocate<StaticGlobalSpaceRegion>(); |
| new (R) StaticGlobalSpaceRegion(this, CR); |
| return R; |
| } |
| |
| const HeapSpaceRegion *MemRegionManager::getHeapRegion() { |
| return LazyAllocate(heap); |
| } |
| |
| const UnknownSpaceRegion *MemRegionManager::getUnknownRegion() { |
| return LazyAllocate(unknown); |
| } |
| |
| const CodeSpaceRegion *MemRegionManager::getCodeRegion() { |
| return LazyAllocate(code); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Constructing regions. |
| //===----------------------------------------------------------------------===// |
| |
| const StringRegion *MemRegionManager::getStringRegion(const StringLiteral *Str){ |
| return getSubRegion<StringRegion>( |
| Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); |
| } |
| |
| const ObjCStringRegion * |
| MemRegionManager::getObjCStringRegion(const ObjCStringLiteral *Str){ |
| return getSubRegion<ObjCStringRegion>( |
| Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); |
| } |
| |
| /// Look through a chain of LocationContexts to either find the |
| /// StackFrameContext that matches a DeclContext, or find a VarRegion |
| /// for a variable captured by a block. |
| static llvm::PointerUnion<const StackFrameContext *, const VarRegion *> |
| getStackOrCaptureRegionForDeclContext(const LocationContext *LC, |
| const DeclContext *DC, |
| const VarDecl *VD) { |
| while (LC) { |
| if (const auto *SFC = dyn_cast<StackFrameContext>(LC)) { |
| if (cast<DeclContext>(SFC->getDecl()) == DC) |
| return SFC; |
| } |
| if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) { |
| const auto *BR = |
| static_cast<const BlockDataRegion *>(BC->getContextData()); |
| // FIXME: This can be made more efficient. |
| for (BlockDataRegion::referenced_vars_iterator |
| I = BR->referenced_vars_begin(), |
| E = BR->referenced_vars_end(); I != E; ++I) { |
| const VarRegion *VR = I.getOriginalRegion(); |
| if (VR->getDecl() == VD) |
| return cast<VarRegion>(I.getCapturedRegion()); |
| } |
| } |
| |
| LC = LC->getParent(); |
| } |
| return (const StackFrameContext *)nullptr; |
| } |
| |
| const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, |
| const LocationContext *LC) { |
| D = D->getCanonicalDecl(); |
| const MemRegion *sReg = nullptr; |
| |
| if (D->hasGlobalStorage() && !D->isStaticLocal()) { |
| |
| // First handle the globals defined in system headers. |
| if (C.getSourceManager().isInSystemHeader(D->getLocation())) { |
| // Whitelist the system globals which often DO GET modified, assume the |
| // rest are immutable. |
| if (D->getName().find("errno") != StringRef::npos) |
| sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); |
| else |
| sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); |
| |
| // Treat other globals as GlobalInternal unless they are constants. |
| } else { |
| QualType GQT = D->getType(); |
| const Type *GT = GQT.getTypePtrOrNull(); |
| // TODO: We could walk the complex types here and see if everything is |
| // constified. |
| if (GT && GQT.isConstQualified() && GT->isArithmeticType()) |
| sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); |
| else |
| sReg = getGlobalsRegion(); |
| } |
| |
| // Finally handle static locals. |
| } else { |
| // FIXME: Once we implement scope handling, we will need to properly lookup |
| // 'D' to the proper LocationContext. |
| const DeclContext *DC = D->getDeclContext(); |
| llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V = |
| getStackOrCaptureRegionForDeclContext(LC, DC, D); |
| |
| if (V.is<const VarRegion*>()) |
| return V.get<const VarRegion*>(); |
| |
| const auto *STC = V.get<const StackFrameContext *>(); |
| |
| if (!STC) { |
| // FIXME: Assign a more sensible memory space to static locals |
| // we see from within blocks that we analyze as top-level declarations. |
| sReg = getUnknownRegion(); |
| } else { |
| if (D->hasLocalStorage()) { |
| sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D) |
| ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC)) |
| : static_cast<const MemRegion*>(getStackLocalsRegion(STC)); |
| } |
| else { |
| assert(D->isStaticLocal()); |
| const Decl *STCD = STC->getDecl(); |
| if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD)) |
| sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, |
| getFunctionCodeRegion(cast<NamedDecl>(STCD))); |
| else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) { |
| // FIXME: The fallback type here is totally bogus -- though it should |
| // never be queried, it will prevent uniquing with the real |
| // BlockCodeRegion. Ideally we'd fix the AST so that we always had a |
| // signature. |
| QualType T; |
| if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten()) |
| T = TSI->getType(); |
| if (T.isNull()) |
| T = getContext().VoidTy; |
| if (!T->getAs<FunctionType>()) |
| T = getContext().getFunctionNoProtoType(T); |
| T = getContext().getBlockPointerType(T); |
| |
| const BlockCodeRegion *BTR = |
| getBlockCodeRegion(BD, C.getCanonicalType(T), |
| STC->getAnalysisDeclContext()); |
| sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, |
| BTR); |
| } |
| else { |
| sReg = getGlobalsRegion(); |
| } |
| } |
| } |
| } |
| |
| return getSubRegion<VarRegion>(D, sReg); |
| } |
| |
| const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, |
| const MemRegion *superR) { |
| D = D->getCanonicalDecl(); |
| return getSubRegion<VarRegion>(D, superR); |
| } |
| |
| const BlockDataRegion * |
| MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, |
| const LocationContext *LC, |
| unsigned blockCount) { |
| const MemSpaceRegion *sReg = nullptr; |
| const BlockDecl *BD = BC->getDecl(); |
| if (!BD->hasCaptures()) { |
| // This handles 'static' blocks. |
| sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); |
| } |
| else { |
| if (LC) { |
| // FIXME: Once we implement scope handling, we want the parent region |
| // to be the scope. |
| const StackFrameContext *STC = LC->getStackFrame(); |
| assert(STC); |
| sReg = getStackLocalsRegion(STC); |
| } |
| else { |
| // We allow 'LC' to be NULL for cases where want BlockDataRegions |
| // without context-sensitivity. |
| sReg = getUnknownRegion(); |
| } |
| } |
| |
| return getSubRegion<BlockDataRegion>(BC, LC, blockCount, sReg); |
| } |
| |
| const CXXTempObjectRegion * |
| MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) { |
| return getSubRegion<CXXTempObjectRegion>( |
| Ex, getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind, nullptr)); |
| } |
| |
| const CompoundLiteralRegion* |
| MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, |
| const LocationContext *LC) { |
| const MemSpaceRegion *sReg = nullptr; |
| |
| if (CL->isFileScope()) |
| sReg = getGlobalsRegion(); |
| else { |
| const StackFrameContext *STC = LC->getStackFrame(); |
| assert(STC); |
| sReg = getStackLocalsRegion(STC); |
| } |
| |
| return getSubRegion<CompoundLiteralRegion>(CL, sReg); |
| } |
| |
| const ElementRegion* |
| MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, |
| const SubRegion* superRegion, |
| ASTContext &Ctx){ |
| QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType(); |
| |
| llvm::FoldingSetNodeID ID; |
| ElementRegion::ProfileRegion(ID, T, Idx, superRegion); |
| |
| void *InsertPos; |
| MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); |
| auto *R = cast_or_null<ElementRegion>(data); |
| |
| if (!R) { |
| R = A.Allocate<ElementRegion>(); |
| new (R) ElementRegion(T, Idx, superRegion); |
| Regions.InsertNode(R, InsertPos); |
| } |
| |
| return R; |
| } |
| |
| const FunctionCodeRegion * |
| MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { |
| // To think: should we canonicalize the declaration here? |
| return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion()); |
| } |
| |
| const BlockCodeRegion * |
| MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, |
| AnalysisDeclContext *AC) { |
| return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion()); |
| } |
| |
| /// getSymbolicRegion - Retrieve or create a "symbolic" memory region. |
| const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { |
| return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); |
| } |
| |
| const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { |
| return getSubRegion<SymbolicRegion>(Sym, getHeapRegion()); |
| } |
| |
| const FieldRegion* |
| MemRegionManager::getFieldRegion(const FieldDecl *d, |
| const SubRegion* superRegion){ |
| return getSubRegion<FieldRegion>(d, superRegion); |
| } |
| |
| const ObjCIvarRegion* |
| MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d, |
| const SubRegion* superRegion) { |
| return getSubRegion<ObjCIvarRegion>(d, superRegion); |
| } |
| |
| const CXXTempObjectRegion* |
| MemRegionManager::getCXXTempObjectRegion(Expr const *E, |
| LocationContext const *LC) { |
| const StackFrameContext *SFC = LC->getStackFrame(); |
| assert(SFC); |
| return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC)); |
| } |
| |
| /// Checks whether \p BaseClass is a valid virtual or direct non-virtual base |
| /// class of the type of \p Super. |
| static bool isValidBaseClass(const CXXRecordDecl *BaseClass, |
| const TypedValueRegion *Super, |
| bool IsVirtual) { |
| BaseClass = BaseClass->getCanonicalDecl(); |
| |
| const CXXRecordDecl *Class = Super->getValueType()->getAsCXXRecordDecl(); |
| if (!Class) |
| return true; |
| |
| if (IsVirtual) |
| return Class->isVirtuallyDerivedFrom(BaseClass); |
| |
| for (const auto &I : Class->bases()) { |
| if (I.getType()->getAsCXXRecordDecl()->getCanonicalDecl() == BaseClass) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| const CXXBaseObjectRegion * |
| MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, |
| const SubRegion *Super, |
| bool IsVirtual) { |
| if (isa<TypedValueRegion>(Super)) { |
| assert(isValidBaseClass(RD, cast<TypedValueRegion>(Super), IsVirtual)); |
| (void)&isValidBaseClass; |
| |
| if (IsVirtual) { |
| // Virtual base regions should not be layered, since the layout rules |
| // are different. |
| while (const auto *Base = dyn_cast<CXXBaseObjectRegion>(Super)) |
| Super = cast<SubRegion>(Base->getSuperRegion()); |
| assert(Super && !isa<MemSpaceRegion>(Super)); |
| } |
| } |
| |
| return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super); |
| } |
| |
| const CXXDerivedObjectRegion * |
| MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD, |
| const SubRegion *Super) { |
| return getSubRegion<CXXDerivedObjectRegion>(RD, Super); |
| } |
| |
| const CXXThisRegion* |
| MemRegionManager::getCXXThisRegion(QualType thisPointerTy, |
| const LocationContext *LC) { |
| const auto *PT = thisPointerTy->getAs<PointerType>(); |
| assert(PT); |
| // Inside the body of the operator() of a lambda a this expr might refer to an |
| // object in one of the parent location contexts. |
| const auto *D = dyn_cast<CXXMethodDecl>(LC->getDecl()); |
| // FIXME: when operator() of lambda is analyzed as a top level function and |
| // 'this' refers to a this to the enclosing scope, there is no right region to |
| // return. |
| while (!LC->inTopFrame() && (!D || D->isStatic() || |
| PT != D->getThisType()->getAs<PointerType>())) { |
| LC = LC->getParent(); |
| D = dyn_cast<CXXMethodDecl>(LC->getDecl()); |
| } |
| const StackFrameContext *STC = LC->getStackFrame(); |
| assert(STC); |
| return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC)); |
| } |
| |
| const AllocaRegion* |
| MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt, |
| const LocationContext *LC) { |
| const StackFrameContext *STC = LC->getStackFrame(); |
| assert(STC); |
| return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC)); |
| } |
| |
| const MemSpaceRegion *MemRegion::getMemorySpace() const { |
| const MemRegion *R = this; |
| const auto *SR = dyn_cast<SubRegion>(this); |
| |
| while (SR) { |
| R = SR->getSuperRegion(); |
| SR = dyn_cast<SubRegion>(R); |
| } |
| |
| return dyn_cast<MemSpaceRegion>(R); |
| } |
| |
| bool MemRegion::hasStackStorage() const { |
| return isa<StackSpaceRegion>(getMemorySpace()); |
| } |
| |
| bool MemRegion::hasStackNonParametersStorage() const { |
| return isa<StackLocalsSpaceRegion>(getMemorySpace()); |
| } |
| |
| bool MemRegion::hasStackParametersStorage() const { |
| return isa<StackArgumentsSpaceRegion>(getMemorySpace()); |
| } |
| |
| bool MemRegion::hasGlobalsOrParametersStorage() const { |
| const MemSpaceRegion *MS = getMemorySpace(); |
| return isa<StackArgumentsSpaceRegion>(MS) || |
| isa<GlobalsSpaceRegion>(MS); |
| } |
| |
| // getBaseRegion strips away all elements and fields, and get the base region |
| // of them. |
| const MemRegion *MemRegion::getBaseRegion() const { |
| const MemRegion *R = this; |
| while (true) { |
| switch (R->getKind()) { |
| case MemRegion::ElementRegionKind: |
| case MemRegion::FieldRegionKind: |
| case MemRegion::ObjCIvarRegionKind: |
| case MemRegion::CXXBaseObjectRegionKind: |
| case MemRegion::CXXDerivedObjectRegionKind: |
| R = cast<SubRegion>(R)->getSuperRegion(); |
| continue; |
| default: |
| break; |
| } |
| break; |
| } |
| return R; |
| } |
| |
| // getgetMostDerivedObjectRegion gets the region of the root class of a C++ |
| // class hierarchy. |
| const MemRegion *MemRegion::getMostDerivedObjectRegion() const { |
| const MemRegion *R = this; |
| while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R)) |
| R = BR->getSuperRegion(); |
| return R; |
| } |
| |
| bool MemRegion::isSubRegionOf(const MemRegion *) const { |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // View handling. |
| //===----------------------------------------------------------------------===// |
| |
| const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const { |
| const MemRegion *R = this; |
| while (true) { |
| switch (R->getKind()) { |
| case ElementRegionKind: { |
| const auto *ER = cast<ElementRegion>(R); |
| if (!ER->getIndex().isZeroConstant()) |
| return R; |
| R = ER->getSuperRegion(); |
| break; |
| } |
| case CXXBaseObjectRegionKind: |
| case CXXDerivedObjectRegionKind: |
| if (!StripBaseAndDerivedCasts) |
| return R; |
| R = cast<TypedValueRegion>(R)->getSuperRegion(); |
| break; |
| default: |
| return R; |
| } |
| } |
| } |
| |
| const SymbolicRegion *MemRegion::getSymbolicBase() const { |
| const auto *SubR = dyn_cast<SubRegion>(this); |
| |
| while (SubR) { |
| if (const auto *SymR = dyn_cast<SymbolicRegion>(SubR)) |
| return SymR; |
| SubR = dyn_cast<SubRegion>(SubR->getSuperRegion()); |
| } |
| return nullptr; |
| } |
| |
| RegionRawOffset ElementRegion::getAsArrayOffset() const { |
| int64_t offset = 0; |
| const ElementRegion *ER = this; |
| const MemRegion *superR = nullptr; |
| ASTContext &C = getContext(); |
| |
| // FIXME: Handle multi-dimensional arrays. |
| |
| while (ER) { |
| superR = ER->getSuperRegion(); |
| |
| // FIXME: generalize to symbolic offsets. |
| SVal index = ER->getIndex(); |
| if (auto CI = index.getAs<nonloc::ConcreteInt>()) { |
| // Update the offset. |
| int64_t i = CI->getValue().getSExtValue(); |
| |
| if (i != 0) { |
| QualType elemType = ER->getElementType(); |
| |
| // If we are pointing to an incomplete type, go no further. |
| if (elemType->isIncompleteType()) { |
| superR = ER; |
| break; |
| } |
| |
| int64_t size = C.getTypeSizeInChars(elemType).getQuantity(); |
| if (auto NewOffset = llvm::checkedMulAdd(i, size, offset)) { |
| offset = *NewOffset; |
| } else { |
| LLVM_DEBUG(llvm::dbgs() << "MemRegion::getAsArrayOffset: " |
| << "offset overflowing, returning unknown\n"); |
| |
| return nullptr; |
| } |
| } |
| |
| // Go to the next ElementRegion (if any). |
| ER = dyn_cast<ElementRegion>(superR); |
| continue; |
| } |
| |
| return nullptr; |
| } |
| |
| assert(superR && "super region cannot be NULL"); |
| return RegionRawOffset(superR, CharUnits::fromQuantity(offset)); |
| } |
| |
| /// Returns true if \p Base is an immediate base class of \p Child |
| static bool isImmediateBase(const CXXRecordDecl *Child, |
| const CXXRecordDecl *Base) { |
| assert(Child && "Child must not be null"); |
| // Note that we do NOT canonicalize the base class here, because |
| // ASTRecordLayout doesn't either. If that leads us down the wrong path, |
| // so be it; at least we won't crash. |
| for (const auto &I : Child->bases()) { |
| if (I.getType()->getAsCXXRecordDecl() == Base) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static RegionOffset calculateOffset(const MemRegion *R) { |
| const MemRegion *SymbolicOffsetBase = nullptr; |
| int64_t Offset = 0; |
| |
| while (true) { |
| switch (R->getKind()) { |
| case MemRegion::CodeSpaceRegionKind: |
| case MemRegion::StackLocalsSpaceRegionKind: |
| case MemRegion::StackArgumentsSpaceRegionKind: |
| case MemRegion::HeapSpaceRegionKind: |
| case MemRegion::UnknownSpaceRegionKind: |
| case MemRegion::StaticGlobalSpaceRegionKind: |
| case MemRegion::GlobalInternalSpaceRegionKind: |
| case MemRegion::GlobalSystemSpaceRegionKind: |
| case MemRegion::GlobalImmutableSpaceRegionKind: |
| // Stores can bind directly to a region space to set a default value. |
| assert(Offset == 0 && !SymbolicOffsetBase); |
| goto Finish; |
| |
| case MemRegion::FunctionCodeRegionKind: |
| case MemRegion::BlockCodeRegionKind: |
| case MemRegion::BlockDataRegionKind: |
| // These will never have bindings, but may end up having values requested |
| // if the user does some strange casting. |
| if (Offset != 0) |
| SymbolicOffsetBase = R; |
| goto Finish; |
| |
| case MemRegion::SymbolicRegionKind: |
| case MemRegion::AllocaRegionKind: |
| case MemRegion::CompoundLiteralRegionKind: |
| case MemRegion::CXXThisRegionKind: |
| case MemRegion::StringRegionKind: |
| case MemRegion::ObjCStringRegionKind: |
| case MemRegion::VarRegionKind: |
| case MemRegion::CXXTempObjectRegionKind: |
| // Usual base regions. |
| goto Finish; |
| |
| case MemRegion::ObjCIvarRegionKind: |
| // This is a little strange, but it's a compromise between |
| // ObjCIvarRegions having unknown compile-time offsets (when using the |
| // non-fragile runtime) and yet still being distinct, non-overlapping |
| // regions. Thus we treat them as "like" base regions for the purposes |
| // of computing offsets. |
| goto Finish; |
| |
| case MemRegion::CXXBaseObjectRegionKind: { |
| const auto *BOR = cast<CXXBaseObjectRegion>(R); |
| R = BOR->getSuperRegion(); |
| |
| QualType Ty; |
| bool RootIsSymbolic = false; |
| if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) { |
| Ty = TVR->getDesugaredValueType(R->getContext()); |
| } else if (const auto *SR = dyn_cast<SymbolicRegion>(R)) { |
| // If our base region is symbolic, we don't know what type it really is. |
| // Pretend the type of the symbol is the true dynamic type. |
| // (This will at least be self-consistent for the life of the symbol.) |
| Ty = SR->getSymbol()->getType()->getPointeeType(); |
| RootIsSymbolic = true; |
| } |
| |
| const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); |
| if (!Child) { |
| // We cannot compute the offset of the base class. |
| SymbolicOffsetBase = R; |
| } else { |
| if (RootIsSymbolic) { |
| // Base layers on symbolic regions may not be type-correct. |
| // Double-check the inheritance here, and revert to a symbolic offset |
| // if it's invalid (e.g. due to a reinterpret_cast). |
| if (BOR->isVirtual()) { |
| if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) |
| SymbolicOffsetBase = R; |
| } else { |
| if (!isImmediateBase(Child, BOR->getDecl())) |
| SymbolicOffsetBase = R; |
| } |
| } |
| } |
| |
| // Don't bother calculating precise offsets if we already have a |
| // symbolic offset somewhere in the chain. |
| if (SymbolicOffsetBase) |
| continue; |
| |
| CharUnits BaseOffset; |
| const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(Child); |
| if (BOR->isVirtual()) |
| BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl()); |
| else |
| BaseOffset = Layout.getBaseClassOffset(BOR->getDecl()); |
| |
| // The base offset is in chars, not in bits. |
| Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth(); |
| break; |
| } |
| |
| case MemRegion::CXXDerivedObjectRegionKind: { |
| // TODO: Store the base type in the CXXDerivedObjectRegion and use it. |
| goto Finish; |
| } |
| |
| case MemRegion::ElementRegionKind: { |
| const auto *ER = cast<ElementRegion>(R); |
| R = ER->getSuperRegion(); |
| |
| QualType EleTy = ER->getValueType(); |
| if (EleTy->isIncompleteType()) { |
| // We cannot compute the offset of the base class. |
| SymbolicOffsetBase = R; |
| continue; |
| } |
| |
| SVal Index = ER->getIndex(); |
| if (Optional<nonloc::ConcreteInt> CI = |
| Index.getAs<nonloc::ConcreteInt>()) { |
| // Don't bother calculating precise offsets if we already have a |
| // symbolic offset somewhere in the chain. |
| if (SymbolicOffsetBase) |
| continue; |
| |
| int64_t i = CI->getValue().getSExtValue(); |
| // This type size is in bits. |
| Offset += i * R->getContext().getTypeSize(EleTy); |
| } else { |
| // We cannot compute offset for non-concrete index. |
| SymbolicOffsetBase = R; |
| } |
| break; |
| } |
| case MemRegion::FieldRegionKind: { |
| const auto *FR = cast<FieldRegion>(R); |
| R = FR->getSuperRegion(); |
| assert(R); |
| |
| const RecordDecl *RD = FR->getDecl()->getParent(); |
| if (RD->isUnion() || !RD->isCompleteDefinition()) { |
| // We cannot compute offset for incomplete type. |
| // For unions, we could treat everything as offset 0, but we'd rather |
| // treat each field as a symbolic offset so they aren't stored on top |
| // of each other, since we depend on things in typed regions actually |
| // matching their types. |
| SymbolicOffsetBase = R; |
| } |
| |
| // Don't bother calculating precise offsets if we already have a |
| // symbolic offset somewhere in the chain. |
| if (SymbolicOffsetBase) |
| continue; |
| |
| // Get the field number. |
| unsigned idx = 0; |
| for (RecordDecl::field_iterator FI = RD->field_begin(), |
| FE = RD->field_end(); FI != FE; ++FI, ++idx) { |
| if (FR->getDecl() == *FI) |
| break; |
| } |
| const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(RD); |
| // This is offset in bits. |
| Offset += Layout.getFieldOffset(idx); |
| break; |
| } |
| } |
| } |
| |
| Finish: |
| if (SymbolicOffsetBase) |
| return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic); |
| return RegionOffset(R, Offset); |
| } |
| |
| RegionOffset MemRegion::getAsOffset() const { |
| if (!cachedOffset) |
| cachedOffset = calculateOffset(this); |
| return *cachedOffset; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // BlockDataRegion |
| //===----------------------------------------------------------------------===// |
| |
| std::pair<const VarRegion *, const VarRegion *> |
| BlockDataRegion::getCaptureRegions(const VarDecl *VD) { |
| MemRegionManager &MemMgr = *getMemRegionManager(); |
| const VarRegion *VR = nullptr; |
| const VarRegion *OriginalVR = nullptr; |
| |
| if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) { |
| VR = MemMgr.getVarRegion(VD, this); |
| OriginalVR = MemMgr.getVarRegion(VD, LC); |
| } |
| else { |
| if (LC) { |
| VR = MemMgr.getVarRegion(VD, LC); |
| OriginalVR = VR; |
| } |
| else { |
| VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); |
| OriginalVR = MemMgr.getVarRegion(VD, LC); |
| } |
| } |
| return std::make_pair(VR, OriginalVR); |
| } |
| |
| void BlockDataRegion::LazyInitializeReferencedVars() { |
| if (ReferencedVars) |
| return; |
| |
| AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext(); |
| const auto &ReferencedBlockVars = AC->getReferencedBlockVars(BC->getDecl()); |
| auto NumBlockVars = |
| std::distance(ReferencedBlockVars.begin(), ReferencedBlockVars.end()); |
| |
| if (NumBlockVars == 0) { |
| ReferencedVars = (void*) 0x1; |
| return; |
| } |
| |
| MemRegionManager &MemMgr = *getMemRegionManager(); |
| llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); |
| BumpVectorContext BC(A); |
| |
| using VarVec = BumpVector<const MemRegion *>; |
| |
| auto *BV = A.Allocate<VarVec>(); |
| new (BV) VarVec(BC, NumBlockVars); |
| auto *BVOriginal = A.Allocate<VarVec>(); |
| new (BVOriginal) VarVec(BC, NumBlockVars); |
| |
| for (const auto *VD : ReferencedBlockVars) { |
| const VarRegion *VR = nullptr; |
| const VarRegion *OriginalVR = nullptr; |
| std::tie(VR, OriginalVR) = getCaptureRegions(VD); |
| assert(VR); |
| assert(OriginalVR); |
| BV->push_back(VR, BC); |
| BVOriginal->push_back(OriginalVR, BC); |
| } |
| |
| ReferencedVars = BV; |
| OriginalVars = BVOriginal; |
| } |
| |
| BlockDataRegion::referenced_vars_iterator |
| BlockDataRegion::referenced_vars_begin() const { |
| const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); |
| |
| auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars); |
| |
| if (Vec == (void*) 0x1) |
| return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); |
| |
| auto *VecOriginal = |
| static_cast<BumpVector<const MemRegion *> *>(OriginalVars); |
| |
| return BlockDataRegion::referenced_vars_iterator(Vec->begin(), |
| VecOriginal->begin()); |
| } |
| |
| BlockDataRegion::referenced_vars_iterator |
| BlockDataRegion::referenced_vars_end() const { |
| const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); |
| |
| auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars); |
| |
| if (Vec == (void*) 0x1) |
| return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); |
| |
| auto *VecOriginal = |
| static_cast<BumpVector<const MemRegion *> *>(OriginalVars); |
| |
| return BlockDataRegion::referenced_vars_iterator(Vec->end(), |
| VecOriginal->end()); |
| } |
| |
| const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { |
| for (referenced_vars_iterator I = referenced_vars_begin(), |
| E = referenced_vars_end(); |
| I != E; ++I) { |
| if (I.getCapturedRegion() == R) |
| return I.getOriginalRegion(); |
| } |
| return nullptr; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // RegionAndSymbolInvalidationTraits |
| //===----------------------------------------------------------------------===// |
| |
| void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym, |
| InvalidationKinds IK) { |
| SymTraitsMap[Sym] |= IK; |
| } |
| |
| void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, |
| InvalidationKinds IK) { |
| assert(MR); |
| if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) |
| setTrait(SR->getSymbol(), IK); |
| else |
| MRTraitsMap[MR] |= IK; |
| } |
| |
| bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, |
| InvalidationKinds IK) const { |
| const_symbol_iterator I = SymTraitsMap.find(Sym); |
| if (I != SymTraitsMap.end()) |
| return I->second & IK; |
| |
| return false; |
| } |
| |
| bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR, |
| InvalidationKinds IK) const { |
| if (!MR) |
| return false; |
| |
| if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) |
| return hasTrait(SR->getSymbol(), IK); |
| |
| const_region_iterator I = MRTraitsMap.find(MR); |
| if (I != MRTraitsMap.end()) |
| return I->second & IK; |
| |
| return false; |
| } |