diff --git a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h index 43a70f596a4da..519d2d5b3676b 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h +++ b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h @@ -27,6 +27,7 @@ namespace ento { class SValExplainer : public FullSValVisitor { private: ASTContext &ACtx; + ProgramStateRef State; std::string printStmt(const Stmt *S) { std::string Str; @@ -55,7 +56,8 @@ class SValExplainer : public FullSValVisitor { } public: - SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {} + SValExplainer(ASTContext &Ctx, ProgramStateRef State) + : ACtx(Ctx), State(State) {} std::string VisitUnknownVal(UnknownVal V) { return "unknown value"; @@ -166,7 +168,7 @@ class SValExplainer : public FullSValVisitor { .getCanonicalType()->getAs()) return "object at " + Visit(R->getSymbol()); // Other heap-based symbolic regions are also special. - if (isa(R->getMemorySpace())) + if (R->hasMemorySpace(State)) return "heap segment that starts at " + Visit(R->getSymbol()); return "pointee of " + Visit(R->getSymbol()); } diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 796fb43a2fc66..89d306fb94046 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -26,6 +26,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/DenseMap.h" @@ -119,7 +120,40 @@ class MemRegion : public llvm::FoldingSetNode { virtual MemRegionManager &getMemRegionManager() const = 0; - LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion *getMemorySpace() const; + /// Deprecated. Gets the 'raw' memory space of a memory region's base region. + /// If the MemRegion is originally associated with Unknown memspace, then the + /// State may have a more accurate memspace for this region. + /// Use getMemorySpace(ProgramStateRef) instead. + [[nodiscard]] LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * + getRawMemorySpace() const; + + /// Deprecated. Use getMemorySpace(ProgramStateRef) instead. + template + [[nodiscard]] const MemSpace *getRawMemorySpaceAs() const { + return dyn_cast(getRawMemorySpace()); + } + + /// Returns the most specific memory space for this memory region in the given + /// ProgramStateRef. We may infer a more accurate memory space for unknown + /// space regions and associate this in the State. + [[nodiscard]] LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * + getMemorySpace(ProgramStateRef State) const; + + template + [[nodiscard]] const MemSpace *getMemorySpaceAs(ProgramStateRef State) const { + return dyn_cast(getMemorySpace(State)); + } + + template + [[nodiscard]] bool hasMemorySpace(ProgramStateRef State) const { + static_assert(sizeof...(MemorySpaces)); + return isa(getMemorySpace(State)); + } + + /// Set the dynamically deduced memory space of a MemRegion that currently has + /// UnknownSpaceRegion. \p Space shouldn't be UnknownSpaceRegion. + [[nodiscard]] ProgramStateRef + setMemorySpace(ProgramStateRef State, const MemSpaceRegion *Space) const; LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion *getBaseRegion() const; @@ -140,12 +174,6 @@ class MemRegion : public llvm::FoldingSetNode { /// It might return null. const SymbolicRegion *getSymbolicBase() const; - bool hasStackStorage() const; - - bool hasStackNonParametersStorage() const; - - bool hasStackParametersStorage() const; - /// Compute the offset within the top level memory object. RegionOffset getAsOffset() const; diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 954b4763034e7..7fdd58726e11b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -78,6 +78,7 @@ determineElementSize(const std::optional T, const CheckerContext &C) { } class StateUpdateReporter { + const MemSpaceRegion *Space; const SubRegion *Reg; const NonLoc ByteOffsetVal; const std::optional ElementType; @@ -88,8 +89,8 @@ class StateUpdateReporter { public: StateUpdateReporter(const SubRegion *R, NonLoc ByteOffsVal, const Expr *E, CheckerContext &C) - : Reg(R), ByteOffsetVal(ByteOffsVal), - ElementType(determineElementType(E, C)), + : Space(R->getMemorySpace(C.getState())), Reg(R), + ByteOffsetVal(ByteOffsVal), ElementType(determineElementType(E, C)), ElementSize(determineElementSize(ElementType, C)) {} void recordNonNegativeAssumption() { AssumedNonNegative = true; } @@ -352,7 +353,8 @@ compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, return {nullptr, nullptr}; } -static std::string getRegionName(const SubRegion *Region) { +static std::string getRegionName(const MemSpaceRegion *Space, + const SubRegion *Region) { if (std::string RegName = Region->getDescriptiveName(); !RegName.empty()) return RegName; @@ -367,8 +369,7 @@ static std::string getRegionName(const SubRegion *Region) { if (isa(Region)) return "the memory returned by 'alloca'"; - if (isa(Region) && - isa(Region->getMemorySpace())) + if (isa(Region) && isa(Space)) return "the heap area"; if (isa(Region)) @@ -388,8 +389,9 @@ static std::optional getConcreteValue(std::optional SV) { return SV ? getConcreteValue(*SV) : std::nullopt; } -static Messages getPrecedesMsgs(const SubRegion *Region, NonLoc Offset) { - std::string RegName = getRegionName(Region), OffsetStr = ""; +static Messages getPrecedesMsgs(const MemSpaceRegion *Space, + const SubRegion *Region, NonLoc Offset) { + std::string RegName = getRegionName(Space, Region), OffsetStr = ""; if (auto ConcreteOffset = getConcreteValue(Offset)) OffsetStr = formatv(" {0}", ConcreteOffset); @@ -418,10 +420,11 @@ static bool tryDividePair(std::optional &Val1, return true; } -static Messages getExceedsMsgs(ASTContext &ACtx, const SubRegion *Region, - NonLoc Offset, NonLoc Extent, SVal Location, +static Messages getExceedsMsgs(ASTContext &ACtx, const MemSpaceRegion *Space, + const SubRegion *Region, NonLoc Offset, + NonLoc Extent, SVal Location, bool AlsoMentionUnderflow) { - std::string RegName = getRegionName(Region); + std::string RegName = getRegionName(Space, Region); const auto *EReg = Location.getAsRegion()->getAs(); assert(EReg && "this checker only handles element access"); QualType ElemType = EReg->getElementType(); @@ -468,9 +471,10 @@ static Messages getExceedsMsgs(ASTContext &ACtx, const SubRegion *Region, std::string(Buf)}; } -static Messages getTaintMsgs(const SubRegion *Region, const char *OffsetName, +static Messages getTaintMsgs(const MemSpaceRegion *Space, + const SubRegion *Region, const char *OffsetName, bool AlsoMentionUnderflow) { - std::string RegName = getRegionName(Region); + std::string RegName = getRegionName(Space, Region); return {formatv("Potential out of bound access to {0} with tainted {1}", RegName, OffsetName), formatv("Access of {0} with a tainted {1} that may be {2}too large", @@ -539,7 +543,7 @@ std::string StateUpdateReporter::getMessage(PathSensitiveBugReport &BR) const { << "' elements in "; else Out << "the extent of "; - Out << getRegionName(Reg); + Out << getRegionName(Space, Reg); } return std::string(Out.str()); } @@ -589,7 +593,7 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const { StateUpdateReporter SUR(Reg, ByteOffset, E, C); // CHECK LOWER BOUND - const MemSpaceRegion *Space = Reg->getMemorySpace(); + const MemSpaceRegion *Space = Reg->getMemorySpace(State); if (!(isa(Reg) && isa(Space))) { // A symbolic region in unknown space represents an unknown pointer that // may point into the middle of an array, so we don't look for underflows. @@ -632,7 +636,7 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const { } else { if (!WithinLowerBound) { // ...and it cannot be valid (>= 0), so report an error. - Messages Msgs = getPrecedesMsgs(Reg, ByteOffset); + Messages Msgs = getPrecedesMsgs(Space, Reg, ByteOffset); reportOOB(C, PrecedesLowerBound, Msgs, ByteOffset, std::nullopt); return; } @@ -675,8 +679,8 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const { } Messages Msgs = - getExceedsMsgs(C.getASTContext(), Reg, ByteOffset, *KnownSize, - Location, AlsoMentionUnderflow); + getExceedsMsgs(C.getASTContext(), Space, Reg, ByteOffset, + *KnownSize, Location, AlsoMentionUnderflow); reportOOB(C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize); return; } @@ -692,7 +696,8 @@ void ArrayBoundChecker::performCheck(const Expr *E, CheckerContext &C) const { if (isTainted(State, ASE->getIdx(), C.getLocationContext())) OffsetName = "index"; - Messages Msgs = getTaintMsgs(Reg, OffsetName, AlsoMentionUnderflow); + Messages Msgs = + getTaintMsgs(Space, Reg, OffsetName, AlsoMentionUnderflow); reportOOB(C, ExceedsUpperBound, Msgs, ByteOffset, KnownSize, /*IsTaintBug=*/true); return; diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp index beb3c8574b5fd..4682b67b510ea 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp @@ -231,7 +231,7 @@ ProgramStateRef ErrnoChecker::checkRegionChanges( // Always reset errno state when the system memory space is invalidated. // The ErrnoRegion is not always found in the list in this case. - if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace())) + if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace(State))) return clearErrnoState(State); return State; diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 28898bb370823..0b6c5163a1637 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -257,7 +257,7 @@ void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, return; SVal V = C.getSVal(Arg); - SValExplainer Ex(C.getASTContext()); + SValExplainer Ex(C.getASTContext(), C.getState()); reportBug(Ex.Visit(V), C); } diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index 9757a00f1fb2f..bb4446df87dbb 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -134,8 +134,8 @@ static const ParmVarDecl *getOriginParam(SVal V, CheckerContext &C, // routine arguments in the heap. while (const MemRegion *MR = Sym->getOriginRegion()) { const auto *VR = dyn_cast(MR); - if (VR && VR->hasStackParametersStorage() && - VR->getStackFrame()->inTopFrame()) + if (VR && VR->hasMemorySpace(C.getState()) && + VR->getStackFrame()->inTopFrame()) return cast(VR->getDecl()); const SymbolicRegion *SR = MR->getSymbolicBase(); diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 754b167642961..d7173d7a464e9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -76,9 +76,8 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, return; // Global variables are fine. - const MemRegion *RB = R->getBaseRegion(); - const MemSpaceRegion *RS = RB->getMemorySpace(); - if (isa(RS)) + const MemSpaceRegion *Space = R->getMemorySpace(C.getState()); + if (isa(Space)) return; // Handle _dispatch_once. In some versions of the OS X SDK we have the case @@ -95,7 +94,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, llvm::raw_svector_ostream os(S); bool SuggestStatic = false; os << "Call to '" << FName << "' uses"; - if (const VarRegion *VR = dyn_cast(RB)) { + if (const VarRegion *VR = dyn_cast(R->getBaseRegion())) { const VarDecl *VD = VR->getDecl(); // FIXME: These should have correct memory space and thus should be filtered // out earlier. This branch only fires when we're looking from a block, @@ -117,9 +116,9 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (IVR != R) os << " memory within"; os << " the instance variable '" << IVR->getDecl()->getName() << '\''; - } else if (isa(RS)) { + } else if (isa(Space)) { os << " heap-allocated memory"; - } else if (isa(RS)) { + } else if (isa(Space)) { // Presence of an IVar superregion has priority over this branch, because // ObjC objects are on the heap even if the core doesn't realize this. // Presence of a block variable base region has priority over this branch, diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 4166cf14391e2..0d1213ddf8b01 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -784,7 +784,8 @@ class MallocChecker bool IsALeakCheck = false) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); - static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); + static bool SummarizeRegion(ProgramStateRef State, raw_ostream &os, + const MemRegion *MR); void HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, @@ -2202,11 +2203,9 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, return nullptr; } - const MemSpaceRegion *MS = R->getMemorySpace(); - // Parameters, locals, statics, globals, and memory returned by // __builtin_alloca() shouldn't be freed. - if (!isa(MS)) { + if (!R->hasMemorySpace(State)) { // Regions returned by malloc() are represented by SymbolicRegion objects // within HeapSpaceRegion. Of course, free() can work on memory allocated // outside the current function, so UnknownSpaceRegion is also a @@ -2384,7 +2383,7 @@ bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { return true; } -bool MallocChecker::SummarizeRegion(raw_ostream &os, +bool MallocChecker::SummarizeRegion(ProgramStateRef State, raw_ostream &os, const MemRegion *MR) { switch (MR->getKind()) { case MemRegion::FunctionCodeRegionKind: { @@ -2403,7 +2402,7 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, os << "a block"; return true; default: { - const MemSpaceRegion *MS = MR->getMemorySpace(); + const MemSpaceRegion *MS = MR->getMemorySpace(State); if (isa(MS)) { const VarRegion *VR = dyn_cast(MR); @@ -2489,8 +2488,8 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, os << "deallocator"; os << " is "; - bool Summarized = MR ? SummarizeRegion(os, MR) - : SummarizeValue(os, ArgVal); + bool Summarized = + MR ? SummarizeRegion(C.getState(), os, MR) : SummarizeValue(os, ArgVal); if (Summarized) os << ", which is not memory allocated by "; else diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index 52416e2139914..6ddefe308013a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -145,12 +145,14 @@ class MoveChecker // Obtains ObjectKind of an object. Because class declaration cannot always // be easily obtained from the memory region, it is supplied separately. - ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const; + ObjectKind classifyObject(ProgramStateRef State, const MemRegion *MR, + const CXXRecordDecl *RD) const; // Classifies the object and dumps a user-friendly description string to // the stream. - void explainObject(llvm::raw_ostream &OS, const MemRegion *MR, - const CXXRecordDecl *RD, MisuseKind MK) const; + void explainObject(ProgramStateRef State, llvm::raw_ostream &OS, + const MemRegion *MR, const CXXRecordDecl *RD, + MisuseKind MK) const; bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const; @@ -299,12 +301,12 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, SmallString<128> Str; llvm::raw_svector_ostream OS(Str); - ObjectKind OK = Chk.classifyObject(Region, RD); + ObjectKind OK = Chk.classifyObject(State, Region, RD); switch (OK.StdKind) { case SK_SmartPtr: if (MK == MK_Dereference) { OS << "Smart pointer"; - Chk.explainObject(OS, Region, RD, MK); + Chk.explainObject(State, OS, Region, RD, MK); OS << " is reset to null when moved from"; break; } @@ -315,12 +317,12 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, case SK_NonStd: case SK_Safe: OS << "Object"; - Chk.explainObject(OS, Region, RD, MK); + Chk.explainObject(State, OS, Region, RD, MK); OS << " is moved"; break; case SK_Unsafe: OS << "Object"; - Chk.explainObject(OS, Region, RD, MK); + Chk.explainObject(State, OS, Region, RD, MK); OS << " is left in a valid but unspecified state after move"; break; } @@ -353,7 +355,7 @@ void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, CheckerContext &C) const { assert(!C.isDifferent() && "No transitions should have been made by now"); const RegionState *RS = State->get(Region); - ObjectKind OK = classifyObject(Region, RD); + ObjectKind OK = classifyObject(State, Region, RD); // Just in case: if it's not a smart pointer but it does have operator *, // we shouldn't call the bug a dereference. @@ -406,24 +408,25 @@ ExplodedNode *MoveChecker::tryToReportBug(const MemRegion *Region, // Creating the error message. llvm::SmallString<128> Str; llvm::raw_svector_ostream OS(Str); + ProgramStateRef State = N->getState(); switch(MK) { case MK_FunCall: OS << "Method called on moved-from object"; - explainObject(OS, Region, RD, MK); + explainObject(State, OS, Region, RD, MK); break; case MK_Copy: OS << "Moved-from object"; - explainObject(OS, Region, RD, MK); + explainObject(State, OS, Region, RD, MK); OS << " is copied"; break; case MK_Move: OS << "Moved-from object"; - explainObject(OS, Region, RD, MK); + explainObject(State, OS, Region, RD, MK); OS << " is moved"; break; case MK_Dereference: OS << "Dereference of null smart pointer"; - explainObject(OS, Region, RD, MK); + explainObject(State, OS, Region, RD, MK); break; } @@ -482,7 +485,7 @@ void MoveChecker::checkPostCall(const CallEvent &Call, return; const CXXRecordDecl *RD = MethodDecl->getParent(); - ObjectKind OK = classifyObject(ArgRegion, RD); + ObjectKind OK = classifyObject(State, ArgRegion, RD); if (shouldBeTracked(OK)) { // Mark object as moved-from. State = State->set(ArgRegion, RegionState::getMoved()); @@ -549,7 +552,7 @@ bool MoveChecker::belongsTo(const CXXRecordDecl *RD, } MoveChecker::ObjectKind -MoveChecker::classifyObject(const MemRegion *MR, +MoveChecker::classifyObject(ProgramStateRef State, const MemRegion *MR, const CXXRecordDecl *RD) const { // Local variables and local rvalue references are classified as "Local". // For the purposes of this checker, we classify move-safe STL types @@ -557,7 +560,7 @@ MoveChecker::classifyObject(const MemRegion *MR, MR = unwrapRValueReferenceIndirection(MR); bool IsLocal = isa_and_nonnull(MR) && - isa(MR->getMemorySpace()); + MR->hasMemorySpace(State); if (!RD || !RD->getDeclContext()->isStdNamespace()) return { IsLocal, SK_NonStd }; @@ -571,8 +574,9 @@ MoveChecker::classifyObject(const MemRegion *MR, return { IsLocal, SK_Unsafe }; } -void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, - const CXXRecordDecl *RD, MisuseKind MK) const { +void MoveChecker::explainObject(ProgramStateRef State, llvm::raw_ostream &OS, + const MemRegion *MR, const CXXRecordDecl *RD, + MisuseKind MK) const { // We may need a leading space every time we actually explain anything, // and we never know if we are to explain anything until we try. if (const auto DR = @@ -581,7 +585,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, OS << " '" << RegionDecl->getDeclName() << "'"; } - ObjectKind OK = classifyObject(MR, RD); + ObjectKind OK = classifyObject(State, MR, RD); switch (OK.StdKind) { case SK_NonStd: case SK_Safe: diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 54870bcb4bb22..15fd9a0b76cc3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -201,9 +201,9 @@ static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { if (std::optional X = val.getAs()) { const MemRegion* R = X->getRegion(); if (const VarRegion *VR = R->getAs()) - if (const StackArgumentsSpaceRegion * - stackReg = dyn_cast(VR->getMemorySpace())) - if (stackReg->getStackFrame() == SFC) + if (const auto *StackSpace = + VR->getMemorySpaceAs(C.getState())) + if (StackSpace->getStackFrame() == SFC) return VR->getValueType(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp index bf81d57bf82fd..ea0fd4165d676 100644 --- a/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp @@ -45,8 +45,11 @@ void PutenvStackArrayChecker::checkPostCall(const CallEvent &Call, SVal ArgV = Call.getArgSVal(0); const Expr *ArgExpr = Call.getArgExpr(0); + if (!ArgV.getAsRegion()) + return; + const auto *SSR = - dyn_cast(ArgV.getAsRegion()->getMemorySpace()); + ArgV.getAsRegion()->getMemorySpaceAs(C.getState()); if (!SSR) return; const auto *StackFrameFuncD = diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 7e74b418b3353..cc1ced7358710 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -501,13 +501,13 @@ static bool isSmartPtrField(const MemRegion *MR) { /// assigned to a struct field, unless it is a known smart pointer /// implementation, about which we know that it is inlined. /// FIXME: This could definitely be improved upon. -static bool shouldEscapeRegion(const MemRegion *R) { +static bool shouldEscapeRegion(ProgramStateRef State, const MemRegion *R) { if (isSmartPtrField(R)) return false; const auto *VR = dyn_cast(R); - if (!R->hasStackStorage() || !VR) + if (!R->hasMemorySpace(State) || !VR) return true; const VarDecl *VD = VR->getDecl(); @@ -561,7 +561,7 @@ updateOutParameters(ProgramStateRef State, const RetainSummary &Summ, if (!Pointee) continue; - if (shouldEscapeRegion(ArgRegion)) + if (shouldEscapeRegion(State, ArgRegion)) continue; auto makeNotOwnedParameter = [&](ProgramStateRef St) { @@ -1141,7 +1141,7 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // Find all symbols referenced by 'val' that we are tracking // and stop tracking them. - if (MR && shouldEscapeRegion(MR)) { + if (MR && shouldEscapeRegion(state, MR)) { state = state->scanReachableSymbols(val).getState(); C.addTransition(state); } diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 456132ef0a0a2..77258958ae027 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -690,7 +690,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, const MemRegion *R = FB.getRegion(); // Do not show local variables belonging to a function other than // where the error is reported. - if (auto MR = dyn_cast(R->getMemorySpace())) + if (const auto *MR = R->getMemorySpaceAs(St)) if (MR->getStackFrame() == LeakContext->getStackFrame()) FirstBinding = R; } diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 2a22e8e10efb0..48fa0ebbfc609 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -19,7 +19,9 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -50,8 +52,6 @@ class StackAddrEscapeChecker void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const; private: - void checkReturnedBlockCaptures(const BlockDataRegion &B, - CheckerContext &C) const; void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B, CheckerContext &C) const; void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion, @@ -59,9 +59,10 @@ class StackAddrEscapeChecker bool isSemaphoreCaptured(const BlockDecl &B) const; static SourceRange genName(raw_ostream &os, const MemRegion *R, ASTContext &Ctx); - static SmallVector + static SmallVector, 4> getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C); - static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C); + static bool isNotInCurrentFrame(const StackSpaceRegion *MS, + CheckerContext &C); }; } // namespace @@ -117,10 +118,9 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, return range; } -bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R, +bool StackAddrEscapeChecker::isNotInCurrentFrame(const StackSpaceRegion *MS, CheckerContext &C) { - const StackSpaceRegion *S = cast(R->getMemorySpace()); - return S->getStackFrame() != C.getStackFrame(); + return MS->getStackFrame() != C.getStackFrame(); } bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const { @@ -134,15 +134,20 @@ bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const { return false; } -SmallVector +SmallVector, 4> StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C) { - SmallVector Regions; + SmallVector, 4> + Regions; + ProgramStateRef State = C.getState(); for (auto Var : B.referenced_vars()) { - SVal Val = C.getState()->getSVal(Var.getCapturedRegion()); - const MemRegion *Region = Val.getAsRegion(); - if (Region && isa(Region->getMemorySpace())) - Regions.push_back(Region); + SVal Val = State->getSVal(Var.getCapturedRegion()); + if (const MemRegion *Region = Val.getAsRegion()) { + if (const auto *Space = + Region->getMemorySpaceAs(State)) { + Regions.emplace_back(Region, Space); + } + } } return Regions; } @@ -198,7 +203,8 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( // a variable of the type "dispatch_semaphore_t". if (isSemaphoreCaptured(*B.getDecl())) return; - for (const MemRegion *Region : getCapturedStackRegions(B, C)) { + for (const MemRegion *Region : + llvm::make_first_range(getCapturedStackRegions(B, C))) { // The block passed to dispatch_async may capture another block // created on the stack. However, there is no leak in this situaton, // no matter if ARC or no ARC is enabled: @@ -272,8 +278,7 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { private: void SaveIfEscapes(const MemRegion *MR) { - const StackSpaceRegion *SSR = - MR->getMemorySpace()->getAs(); + const auto *SSR = MR->getMemorySpaceAs(Ctxt.getState()); if (!SSR) return; @@ -370,19 +375,18 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, EmitReturnLeakError(C, ER, RetE); } -static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) { +static const MemSpaceRegion *getStackOrGlobalSpaceRegion(ProgramStateRef State, + const MemRegion *R) { assert(R); - if (const auto *MemSpace = R->getMemorySpace()) { - if (const auto *SSR = MemSpace->getAs()) - return SSR; - if (const auto *GSR = MemSpace->getAs()) - return GSR; - } + if (const auto *MemSpace = R->getMemorySpace(State); + isa(MemSpace)) + return MemSpace; + // If R describes a lambda capture, it will be a symbolic region // referring to a field region of another symbolic region. if (const auto *SymReg = R->getBaseRegion()->getAs()) { if (const auto *OriginReg = SymReg->getSymbol()->getOriginRegion()) - return getStackOrGlobalSpaceRegion(OriginReg); + return getStackOrGlobalSpaceRegion(State, OriginReg); } return nullptr; } @@ -398,7 +402,8 @@ static const MemRegion *getOriginBaseRegion(const MemRegion *Reg) { return Reg; } -static std::optional printReferrer(const MemRegion *Referrer) { +static std::optional printReferrer(ProgramStateRef State, + const MemRegion *Referrer) { assert(Referrer); const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) { if (isa(Space)) @@ -408,7 +413,7 @@ static std::optional printReferrer(const MemRegion *Referrer) { assert(isa(Space)); // This case covers top-level and inlined analyses. return "caller"; - }(getStackOrGlobalSpaceRegion(Referrer)); + }(getStackOrGlobalSpaceRegion(State, Referrer)); while (!Referrer->canPrintPretty()) { if (const auto *SymReg = dyn_cast(Referrer); @@ -475,6 +480,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; + ProgramStateRef State; const StackFrameContext *PoppedFrame; const bool TopFrame; @@ -483,9 +489,10 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, /// referred by an other stack variable from different stack frame. bool checkForDanglingStackVariable(const MemRegion *Referrer, const MemRegion *Referred) { - const auto *ReferrerMemSpace = getStackOrGlobalSpaceRegion(Referrer); + const auto *ReferrerMemSpace = + getStackOrGlobalSpaceRegion(State, Referrer); const auto *ReferredMemSpace = - Referred->getMemorySpace()->getAs(); + Referred->getMemorySpaceAs(State); if (!ReferrerMemSpace || !ReferredMemSpace) return false; @@ -534,7 +541,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, llvm::SmallPtrSet ExcludedRegions; CallBack(CheckerContext &CC, bool TopFrame) - : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {} + : Ctx(CC), State(CC.getState()), PoppedFrame(CC.getStackFrame()), + TopFrame(TopFrame) {} bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, SVal Val) override { @@ -548,10 +556,15 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, // Check the globals for the same. if (!isa_and_nonnull( - getStackOrGlobalSpaceRegion(Region))) + getStackOrGlobalSpaceRegion(State, Region))) return true; - if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) - V.emplace_back(Region, VR); + + if (VR) { + if (const auto *S = VR->getMemorySpaceAs(State); + S && !isNotInCurrentFrame(S, Ctx)) { + V.emplace_back(Region, VR); + } + } return true; } }; @@ -599,7 +612,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, return; } - auto ReferrerVariable = printReferrer(Referrer); + auto ReferrerVariable = printReferrer(State, Referrer); if (!ReferrerVariable) { continue; } diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 10dfa73cc522d..897e654f3bbc2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -411,7 +411,7 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, // because that's likely to be bad news. ProgramStateRef state = C.getState(); const MemRegion *R = Call.getArgSVal(0).getAsRegion(); - if (!R || !isa(R->getMemorySpace())) + if (!R || !R->hasMemorySpace(state)) return; ExplodedNode *N = C.generateErrorNode(state); @@ -427,7 +427,7 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, os << " stack allocated memory"; os << " for the \"control\" value. Using such transient memory for " "the control value is potentially dangerous."; - if (isa(R) && isa(R->getMemorySpace())) + if (isa(R) && R->hasMemorySpace(state)) os << " Perhaps you intended to declare the variable as 'static'?"; auto report = diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index a6142063895db..f638cb23302c0 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1192,8 +1192,9 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { if (DS->getSingleDecl() != VR->getDecl()) return false; - const MemSpaceRegion *VarSpace = VR->getMemorySpace(); - const auto *FrameSpace = dyn_cast(VarSpace); + const auto *FrameSpace = + VR->getMemorySpaceAs(N->getState()); + if (!FrameSpace) { // If we ever directly evaluate global DeclStmts, this assertion will be // invalid, but this still seems preferable to silently accepting an diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 4a5eeb95511a0..318fa3c1caf06 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -3541,15 +3541,16 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind( for (const std::pair &LocAndVal : LocAndVals) { // Cases (1) and (2). const MemRegion *MR = LocAndVal.first.getAsRegion(); - if (!MR || - !isa(MR->getMemorySpace())) { + const MemSpaceRegion *Space = MR ? MR->getMemorySpace(State) : nullptr; + if (!MR || !isa(Space)) { Escaped.push_back(LocAndVal.second); continue; } // Case (3). if (const auto *VR = dyn_cast(MR->getBaseRegion())) - if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame()) + if (isa(Space) && + VR->getStackFrame()->inTopFrame()) if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl()) if (!RD->hasTrivialDestructor()) { Escaped.push_back(LocAndVal.second); diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index c81bb632b83e9..404ad4958a7f3 100644 --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -61,6 +61,9 @@ using namespace ento; #define DEBUG_TYPE "MemRegion" +REGISTER_MAP_WITH_PROGRAMSTATE(MemSpacesMap, const MemRegion *, + const MemSpaceRegion *) + //===----------------------------------------------------------------------===// // MemRegion Construction. //===----------------------------------------------------------------------===// @@ -163,20 +166,20 @@ MemRegionManager &SubRegion::getMemRegionManager() const { } const StackFrameContext *VarRegion::getStackFrame() const { - const auto *SSR = dyn_cast(getMemorySpace()); + const auto *SSR = dyn_cast(getRawMemorySpace()); return SSR ? SSR->getStackFrame() : nullptr; } const StackFrameContext * CXXLifetimeExtendedObjectRegion::getStackFrame() const { - const auto *SSR = dyn_cast(getMemorySpace()); + const auto *SSR = dyn_cast(getRawMemorySpace()); return SSR ? SSR->getStackFrame() : nullptr; } const StackFrameContext *CXXTempObjectRegion::getStackFrame() const { - assert(isa(getMemorySpace()) && + assert(isa(getRawMemorySpace()) && "A temporary object can only be allocated on the stack"); - return cast(getMemorySpace())->getStackFrame(); + return cast(getRawMemorySpace())->getStackFrame(); } ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg) @@ -1347,7 +1350,7 @@ MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt, return getSubRegion(E, cnt, getStackLocalsRegion(STC)); } -const MemSpaceRegion *MemRegion::getMemorySpace() const { +const MemSpaceRegion *MemRegion::getRawMemorySpace() const { const MemRegion *R = this; const auto *SR = dyn_cast(this); @@ -1359,16 +1362,27 @@ const MemSpaceRegion *MemRegion::getMemorySpace() const { return cast(R); } -bool MemRegion::hasStackStorage() const { - return isa(getMemorySpace()); -} +const MemSpaceRegion *MemRegion::getMemorySpace(ProgramStateRef State) const { + const MemRegion *MR = getBaseRegion(); + + const MemSpaceRegion *RawSpace = MR->getRawMemorySpace(); + if (!isa(RawSpace)) + return RawSpace; -bool MemRegion::hasStackNonParametersStorage() const { - return isa(getMemorySpace()); + const MemSpaceRegion *const *AssociatedSpace = State->get(MR); + return AssociatedSpace ? *AssociatedSpace : RawSpace; } -bool MemRegion::hasStackParametersStorage() const { - return isa(getMemorySpace()); +ProgramStateRef MemRegion::setMemorySpace(ProgramStateRef State, + const MemSpaceRegion *Space) const { + const MemRegion *Base = getBaseRegion(); + + // Shouldn't set unknown space. + assert(!isa(Space)); + + // Currently, it we should have no accurate memspace for this region. + assert(Base->hasMemorySpace(State)); + return State->set(Base, Space); } // Strips away all elements and fields. diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index d01b6ae55f611..5a5a6948098e7 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1300,9 +1300,9 @@ bool InvalidateRegionsWorker::isInitiallyIncludedGlobalRegion( case GFK_None: return false; case GFK_SystemOnly: - return isa(R->getMemorySpace()); + return isa(R->getRawMemorySpace()); case GFK_All: - return isa(R->getMemorySpace()); + return isa(R->getRawMemorySpace()); } llvm_unreachable("unknown globals filter"); @@ -1312,7 +1312,7 @@ bool InvalidateRegionsWorker::includeEntireMemorySpace(const MemRegion *Base) { if (isInitiallyIncludedGlobalRegion(Base)) return true; - const MemSpaceRegion *MemSpace = Base->getMemorySpace(); + const MemSpaceRegion *MemSpace = Base->getRawMemorySpace(); return ITraits.hasTrait(MemSpace, RegionAndSymbolInvalidationTraits::TK_EntireMemSpace); } @@ -1552,7 +1552,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) // The location does not have a bound value. This means that it has // the value it had upon its creation and/or entry to the analyzed // function/method. These are either symbolic values or 'undefined'. - if (R->hasStackNonParametersStorage()) { + if (isa(R->getRawMemorySpace())) { // All stack variables are considered to have undefined values // upon creation. All heap allocated blocks are considered to // have undefined values as well unless they are explicitly bound @@ -2183,7 +2183,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, SR = dyn_cast(Base); } - if (R->hasStackNonParametersStorage()) { + if (isa(R->getRawMemorySpace())) { if (isa(R)) { // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. @@ -2247,7 +2247,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, // Lazily derive a value for the VarRegion. const VarDecl *VD = R->getDecl(); - const MemSpaceRegion *MS = R->getMemorySpace(); + const MemSpaceRegion *MS = R->getRawMemorySpace(); // Arguments are always symbolic. if (isa(MS)) diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index afb0273d23bd4..95d9df4100bfa 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -948,8 +948,8 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, const MemRegion *LeftBase = LeftMR->getBaseRegion(); const MemRegion *RightBase = RightMR->getBaseRegion(); - const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); - const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); + const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(state); + const MemSpaceRegion *RightMS = RightBase->getMemorySpace(state); const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); // If the two regions are from different known memory spaces they cannot be