diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index ce3082757e63..02699531a74b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -1,751 +1,764 @@ //===-- ExprEngine.h - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-=// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines a meta-engine for path-sensitive dataflow analysis that // is built on CoreEngine, but provides the boilerplate to execute transfer // functions and build the ExplodedGraph at the expression level. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPRENGINE_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPRENGINE_H #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" namespace clang { class AnalysisDeclContextManager; class CXXCatchStmt; class CXXConstructExpr; class CXXDeleteExpr; class CXXNewExpr; class CXXTemporaryObjectExpr; class CXXThisExpr; class MaterializeTemporaryExpr; class ObjCAtSynchronizedStmt; class ObjCForCollectionStmt; namespace ento { class AnalysisManager; class CallEvent; class CXXConstructorCall; class ExprEngine : public SubEngine { public: /// The modes of inlining, which override the default analysis-wide settings. enum InliningModes { /// Follow the default settings for inlining callees. Inline_Regular = 0, /// Do minimal inlining of callees. Inline_Minimal = 0x1 }; /// Hints for figuring out of a call should be inlined during evalCall(). struct EvalCallOptions { /// This call is a constructor or a destructor for which we do not currently /// compute the this-region correctly. bool IsCtorOrDtorWithImproperlyModeledTargetRegion = false; /// This call is a constructor or a destructor for a single element within /// an array, a part of array construction or destruction. bool IsArrayCtorOrDtor = false; /// This call is a constructor or a destructor of a temporary value. bool IsTemporaryCtorOrDtor = false; EvalCallOptions() {} }; private: AnalysisManager &AMgr; AnalysisDeclContextManager &AnalysisDeclContexts; CoreEngine Engine; /// G - the simulation graph. ExplodedGraph& G; /// StateMgr - Object that manages the data for all created states. ProgramStateManager StateMgr; /// SymMgr - Object that manages the symbol information. SymbolManager& SymMgr; /// svalBuilder - SValBuilder object that creates SVals from expressions. SValBuilder &svalBuilder; unsigned int currStmtIdx; const NodeBuilderContext *currBldrCtx; /// Helper object to determine if an Objective-C message expression /// implicitly never returns. ObjCNoReturn ObjCNoRet; /// Whether or not GC is enabled in this analysis. bool ObjCGCEnabled; /// The BugReporter associated with this engine. It is important that /// this object be placed at the very end of member variables so that its /// destructor is called before the rest of the ExprEngine is destroyed. GRBugReporter BR; /// The functions which have been analyzed through inlining. This is owned by /// AnalysisConsumer. It can be null. SetOfConstDecls *VisitedCallees; /// The flag, which specifies the mode of inlining for the engine. InliningModes HowToInline; public: ExprEngine(AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS, InliningModes HowToInlineIn); ~ExprEngine() override; /// Returns true if there is still simulation state on the worklist. bool ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) { return Engine.ExecuteWorkList(L, Steps, nullptr); } /// Execute the work list with an initial state. Nodes that reaches the exit /// of the function are added into the Dst set, which represent the exit /// state of the function call. Returns true if there is still simulation /// state on the worklist. bool ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, ProgramStateRef InitState, ExplodedNodeSet &Dst) { return Engine.ExecuteWorkListWithInitialState(L, Steps, InitState, Dst); } /// getContext - Return the ASTContext associated with this analysis. ASTContext &getContext() const { return AMgr.getASTContext(); } AnalysisManager &getAnalysisManager() override { return AMgr; } CheckerManager &getCheckerManager() const { return *AMgr.getCheckerManager(); } SValBuilder &getSValBuilder() { return svalBuilder; } BugReporter& getBugReporter() { return BR; } const NodeBuilderContext &getBuilderContext() { assert(currBldrCtx); return *currBldrCtx; } bool isObjCGCEnabled() { return ObjCGCEnabled; } const Stmt *getStmt() const; void GenerateAutoTransition(ExplodedNode *N); void enqueueEndOfPath(ExplodedNodeSet &S); void GenerateCallExitNode(ExplodedNode *N); /// Visualize the ExplodedGraph created by executing the simulation. void ViewGraph(bool trim = false); /// Visualize a trimmed ExplodedGraph that only contains paths to the given /// nodes. void ViewGraph(ArrayRef Nodes); /// getInitialState - Return the initial state used for the root vertex /// in the ExplodedGraph. ProgramStateRef getInitialState(const LocationContext *InitLoc) override; ExplodedGraph& getGraph() { return G; } const ExplodedGraph& getGraph() const { return G; } /// \brief Run the analyzer's garbage collection - remove dead symbols and /// bindings from the state. /// /// Checkers can participate in this process with two callbacks: /// \c checkLiveSymbols and \c checkDeadSymbols. See the CheckerDocumentation /// class for more information. /// /// \param Node The predecessor node, from which the processing should start. /// \param Out The returned set of output nodes. /// \param ReferenceStmt The statement which is about to be processed. /// Everything needed for this statement should be considered live. /// A null statement means that everything in child LocationContexts /// is dead. /// \param LC The location context of the \p ReferenceStmt. A null location /// context means that we have reached the end of analysis and that /// all statements and local variables should be considered dead. /// \param DiagnosticStmt Used as a location for any warnings that should /// occur while removing the dead (e.g. leaks). By default, the /// \p ReferenceStmt is used. /// \param K Denotes whether this is a pre- or post-statement purge. This /// must only be ProgramPoint::PostStmtPurgeDeadSymbolsKind if an /// entire location context is being cleared, in which case the /// \p ReferenceStmt must either be a ReturnStmt or \c NULL. Otherwise, /// it must be ProgramPoint::PreStmtPurgeDeadSymbolsKind (the default) /// and \p ReferenceStmt must be valid (non-null). void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out, const Stmt *ReferenceStmt, const LocationContext *LC, const Stmt *DiagnosticStmt = nullptr, ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind); /// processCFGElement - Called by CoreEngine. Used to generate new successor /// nodes by processing the 'effects' of a CFG element. void processCFGElement(const CFGElement E, ExplodedNode *Pred, unsigned StmtIdx, NodeBuilderContext *Ctx) override; void ProcessStmt(const Stmt *S, ExplodedNode *Pred); void ProcessLoopExit(const Stmt* S, ExplodedNode *Pred); void ProcessInitializer(const CFGInitializer I, ExplodedNode *Pred); void ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred); void ProcessNewAllocator(const CXXNewExpr *NE, ExplodedNode *Pred); void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); void ProcessDeleteDtor(const CFGDeleteDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); void ProcessBaseDtor(const CFGBaseDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); void ProcessMemberDtor(const CFGMemberDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); void ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Called by CoreEngine when processing the entrance of a CFGBlock. void processCFGBlockEntrance(const BlockEdge &L, NodeBuilderWithSinks &nodeBuilder, ExplodedNode *Pred) override; /// ProcessBranch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. void processBranch(const Stmt *Condition, const Stmt *Term, NodeBuilderContext& BuilderCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) override; /// Called by CoreEngine. /// Used to generate successor nodes for temporary destructors depending /// on whether the corresponding constructor was visited. void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, NodeBuilderContext &BldCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) override; /// Called by CoreEngine. Used to processing branching behavior /// at static initializers. void processStaticInitializer(const DeclStmt *DS, NodeBuilderContext& BuilderCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) override; /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void processIndirectGoto(IndirectGotoNodeBuilder& builder) override; /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void processSwitch(SwitchNodeBuilder& builder) override; /// Called by CoreEngine. Used to notify checkers that processing a /// function has begun. Called for both inlined and and top-level functions. void processBeginOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExplodedNodeSet &Dst, const BlockEdge &L) override; /// Called by CoreEngine. Used to notify checkers that processing a /// function has ended. Called for both inlined and and top-level functions. void processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, const ReturnStmt *RS = nullptr) override; /// Remove dead bindings/symbols before exiting a function. void removeDeadOnEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Generate the entry node of the callee. void processCallEnter(NodeBuilderContext& BC, CallEnter CE, ExplodedNode *Pred) override; /// Generate the sequence of nodes that simulate the call exit and the post /// visit for CallExpr. void processCallExit(ExplodedNode *Pred) override; /// Called by CoreEngine when the analysis worklist has terminated. void processEndWorklist(bool hasWorkRemaining) override; /// evalAssume - Callback function invoked by the ConstraintManager when /// making assumptions about state values. ProgramStateRef processAssume(ProgramStateRef state, SVal cond, bool assumption) override; /// processRegionChanges - Called by ProgramStateManager whenever a change is made /// to the store. Used to update checkers that track region values. ProgramStateRef processRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call) override; /// printState - Called by ProgramStateManager to print checker-specific data. void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep, const LocationContext *LCtx = nullptr) override; ProgramStateManager& getStateManager() override { return StateMgr; } StoreManager& getStoreManager() { return StateMgr.getStoreManager(); } ConstraintManager& getConstraintManager() { return StateMgr.getConstraintManager(); } // FIXME: Remove when we migrate over to just using SValBuilder. BasicValueFactory& getBasicVals() { return StateMgr.getBasicVals(); } // FIXME: Remove when we migrate over to just using ValueManager. SymbolManager& getSymbolManager() { return SymMgr; } const SymbolManager& getSymbolManager() const { return SymMgr; } // Functions for external checking of whether we have unfinished work bool wasBlocksExhausted() const { return Engine.wasBlocksExhausted(); } bool hasEmptyWorkList() const { return !Engine.getWorkList()->hasWork(); } bool hasWorkRemaining() const { return Engine.hasWorkRemaining(); } const CoreEngine &getCoreEngine() const { return Engine; } public: /// Visit - Transfer function logic for all statements. Dispatches to /// other functions that handle specific kinds of statements. void Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitArraySubscriptExpr - Transfer function for array accesses. void VisitArraySubscriptExpr(const ArraySubscriptExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitGCCAsmStmt - Transfer function logic for inline asm. void VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitMSAsmStmt - Transfer function logic for MS inline asm. void VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitBlockExpr - Transfer function logic for BlockExprs. void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitLambdaExpr - Transfer function logic for LambdaExprs. void VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitBinaryOperator - Transfer function logic for binary operators. void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitCall - Transfer function for function calls. void VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitCast - Transfer function logic for all casts (implicit and explicit). void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitCompoundLiteralExpr - Transfer function logic for compound literals. void VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Transfer function logic for DeclRefExprs and BlockDeclRefExprs. void VisitCommonDeclRefExpr(const Expr *DR, const NamedDecl *D, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitDeclStmt - Transfer function logic for DeclStmts. void VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose void VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitInitListExpr(const InitListExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitLogicalExpr - Transfer function logic for '&&', '||' void VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitMemberExpr - Transfer function for member expressions. void VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitAtomicExpr - Transfer function for builtin atomic expressions void VisitAtomicExpr(const AtomicExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Transfer function logic for ObjCAtSynchronizedStmts. void VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Transfer function logic for computing the lvalue of an Objective-C ivar. void VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *DR, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitObjCForCollectionStmt - Transfer function logic for /// ObjCForCollectionStmt. void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitReturnStmt - Transfer function logic for return statements. void VisitReturnStmt(const ReturnStmt *R, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitOffsetOfExpr - Transfer function for offsetof. void VisitOffsetOfExpr(const OffsetOfExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitUnaryExprOrTypeTraitExpr - Transfer function for sizeof. void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// VisitUnaryOperator - Transfer function logic for unary operators. void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Handle ++ and -- (both pre- and post-increment). void VisitIncrementDecrementOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet & Dst); void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, const EvalCallOptions &Options); void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// Create a C++ temporary object for an rvalue. void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst); /// evalEagerlyAssumeBinOpBifurcation - Given the nodes in 'Src', eagerly assume symbolic /// expressions of the form 'x != 0' and generate new nodes (stored in Dst) /// with those assumptions. void evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, const Expr *Ex); std::pair geteagerlyAssumeBinOpBifurcationTags(); SVal evalMinus(SVal X) { return X.isValid() ? svalBuilder.evalMinus(X.castAs()) : X; } SVal evalComplement(SVal X) { return X.isValid() ? svalBuilder.evalComplement(X.castAs()) : X; } ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex, const LocationContext *LCtx, QualType T, QualType ExTy, const CastExpr *CastE, StmtNodeBuilder &Bldr, ExplodedNode *Pred); ProgramStateRef handleLVectorSplat(ProgramStateRef state, const LocationContext *LCtx, const CastExpr *CastE, StmtNodeBuilder &Bldr, ExplodedNode *Pred); void handleUOExtension(ExplodedNodeSet::iterator I, const UnaryOperator* U, StmtNodeBuilder &Bldr); public: SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc L, NonLoc R, QualType T) { return svalBuilder.evalBinOpNN(state, op, L, R, T); } SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc L, SVal R, QualType T) { return R.isValid() ? svalBuilder.evalBinOpNN(state, op, L, R.castAs(), T) : R; } SVal evalBinOp(ProgramStateRef ST, BinaryOperator::Opcode Op, SVal LHS, SVal RHS, QualType T) { return svalBuilder.evalBinOp(ST, Op, LHS, RHS, T); } protected: /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore, VisitDeclStmt, and others. void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, SVal location, SVal Val, bool atDeclInit = false, const ProgramPoint *PP = nullptr); /// Call PointerEscape callback when a value escapes as a result of bind. ProgramStateRef processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val, const LocationContext *LCtx) override; /// Call PointerEscape callback when a value escapes as a result of /// region invalidation. /// \param[in] ITraits Specifies invalidation traits for regions/symbols. ProgramStateRef notifyCheckersOfPointerEscape( ProgramStateRef State, const InvalidatedSymbols *Invalidated, ArrayRef ExplicitRegions, ArrayRef Regions, const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) override; public: // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. // FIXME: Comment on the meaning of the arguments, when 'St' may not // be the same as Pred->state, and when 'location' may not be the // same as state->getLValue(Ex). /// Simulate a read of the result of Ex. void evalLoad(ExplodedNodeSet &Dst, const Expr *NodeEx, /* Eventually will be a CFGStmt */ const Expr *BoundExpr, ExplodedNode *Pred, ProgramStateRef St, SVal location, const ProgramPointTag *tag = nullptr, QualType LoadTy = QualType()); // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. void evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *StoreE, ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val, const ProgramPointTag *tag = nullptr); /// Return the CFG element corresponding to the worklist element /// that is currently being processed by ExprEngine. CFGElement getCurrentCFGElement() { return (*currBldrCtx->getBlock())[currStmtIdx]; } /// \brief Create a new state in which the call return value is binded to the /// call origin expression. ProgramStateRef bindReturnValue(const CallEvent &Call, const LocationContext *LCtx, ProgramStateRef State); /// Evaluate a call, running pre- and post-call checks and allowing checkers /// to be responsible for handling the evaluation of the call itself. void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call); /// \brief Default implementation of call evaluation. void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, const CallEvent &Call, const EvalCallOptions &CallOpts = {}); private: void evalLoadCommon(ExplodedNodeSet &Dst, const Expr *NodeEx, /* Eventually will be a CFGStmt */ const Expr *BoundEx, ExplodedNode *Pred, ProgramStateRef St, SVal location, const ProgramPointTag *tag, QualType LoadTy); // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. void evalLocation(ExplodedNodeSet &Dst, const Stmt *NodeEx, /* This will eventually be a CFGStmt */ const Stmt *BoundEx, ExplodedNode *Pred, ProgramStateRef St, SVal location, const ProgramPointTag *tag, bool isLoad); /// Count the stack depth and determine if the call is recursive. void examineStackFrames(const Decl *D, const LocationContext *LCtx, bool &IsRecursive, unsigned &StackDepth); enum CallInlinePolicy { CIP_Allowed, CIP_DisallowedOnce, CIP_DisallowedAlways }; /// \brief See if a particular call should be inlined, by only looking /// at the call event and the current state of analysis. CallInlinePolicy mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, AnalyzerOptions &Opts, const EvalCallOptions &CallOpts); /// Checks our policies and decides weither the given call should be inlined. bool shouldInlineCall(const CallEvent &Call, const Decl *D, const ExplodedNode *Pred, const EvalCallOptions &CallOpts = {}); bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); /// \brief Conservatively evaluate call by invalidating regions and binding /// a conjured return value. void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); /// \brief Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. void BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC); /// Models a trivial copy or move constructor or trivial assignment operator /// call with a simple bind. void performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &Call); /// If the value of the given expression \p InitWithAdjustments is a NonLoc, /// copy it into a new temporary object region, and replace the value of the /// expression with that. /// /// If \p Result is provided, the new region will be bound to this expression /// instead of \p InitWithAdjustments. ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State, const LocationContext *LC, const Expr *InitWithAdjustments, const Expr *Result = nullptr); /// Returns a region representing the first element of a (possibly /// multi-dimensional) array, for the purposes of element construction or /// destruction. /// /// On return, \p Ty will be set to the base type of the array. /// /// If the type is not an array type at all, the original value is returned. /// Otherwise the "IsArray" flag is set. static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, QualType &Ty, bool &IsArray); /// For a DeclStmt or CXXInitCtorInitializer, walk backward in the current CFG /// block to find the constructor expression that directly constructed into /// the storage for this statement. Returns null if the constructor for this /// statement created a temporary object region rather than directly /// constructing into an existing region. const CXXConstructExpr *findDirectConstructorForCurrentCFGElement(); /// For a given constructor, look forward in the current CFG block to /// determine the region into which an object will be constructed by \p CE. /// When the lookahead fails, a temporary region is returned, and the /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts. const MemRegion *getRegionForConstructedObject(const CXXConstructExpr *CE, ExplodedNode *Pred, const ConstructionContext *CC, EvalCallOptions &CallOpts); /// Store the region of a C++ temporary object corresponding to a /// CXXBindTemporaryExpr for later destruction. static ProgramStateRef addInitializedTemporary( ProgramStateRef State, const CXXBindTemporaryExpr *BTE, const LocationContext *LC, const CXXTempObjectRegion *R); /// Check if all initialized temporary regions are clear for the given /// context range (including FromLC, not including ToLC). /// This is useful for assertions. static bool areInitializedTemporariesClear(ProgramStateRef State, const LocationContext *FromLC, const LocationContext *ToLC); + /// Store the region of a C++ temporary object corresponding to a + /// CXXBindTemporaryExpr for later destruction. + static ProgramStateRef addTemporaryMaterialization( + ProgramStateRef State, const MaterializeTemporaryExpr *MTE, + const LocationContext *LC, const CXXTempObjectRegion *R); + + /// Check if all temporary materialization regions are clear for the given + /// context range (including FromLC, not including ToLC). + /// This is useful for assertions. + static bool areTemporaryMaterializationsClear(ProgramStateRef State, + const LocationContext *FromLC, + const LocationContext *ToLC); + /// Store the region returned by operator new() so that the constructor /// that follows it knew what location to initialize. The value should be /// cleared once the respective CXXNewExpr CFGStmt element is processed. static ProgramStateRef setCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC, SVal V); /// Retrieve the location returned by the current operator new(). static SVal getCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC); /// Clear the location returned by the respective operator new(). This needs /// to be done as soon as CXXNewExpr CFG block is evaluated. static ProgramStateRef clearCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC); /// Check if all allocator values are clear for the given context range /// (including FromLC, not including ToLC). This is useful for assertions. static bool areCXXNewAllocatorValuesClear(ProgramStateRef State, const LocationContext *FromLC, const LocationContext *ToLC); }; /// Traits for storing the call processing policy inside GDM. /// The GDM stores the corresponding CallExpr pointer. // FIXME: This does not use the nice trait macros because it must be accessible // from multiple translation units. struct ReplayWithoutInlining{}; template <> struct ProgramStateTrait : public ProgramStatePartialTrait { static void *GDMIndex() { static int index = 0; return &index; } }; } // end ento namespace } // end clang namespace #endif diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 8808e95aedd6..9f71b2fc03a5 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1,3143 +1,3234 @@ //=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-= // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines a meta-engine for path-sensitive dataflow analysis that // is built on GREngine, but provides the boilerplate to execute transfer // functions and build the ExplodedGraph at the expression level. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "PrettyStackTraceLocationContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/Analysis/CFGStmtMap.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" #endif using namespace clang; using namespace ento; using llvm::APSInt; #define DEBUG_TYPE "ExprEngine" STATISTIC(NumRemoveDeadBindings, "The # of times RemoveDeadBindings is called"); STATISTIC(NumMaxBlockCountReached, "The # of aborted paths due to reaching the maximum block count in " "a top level function"); STATISTIC(NumMaxBlockCountReachedInInlined, "The # of aborted paths due to reaching the maximum block count in " "an inlined function"); STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); typedef llvm::ImmutableMap, const CXXTempObjectRegion *> InitializedTemporariesMap; // Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated. // The StackFrameContext assures that nested calls due to inlined recursive // functions do not interfere. REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporaries, InitializedTemporariesMap) +typedef llvm::ImmutableMap, + const CXXTempObjectRegion *> + TemporaryMaterializationMap; + +// Keeps track of temporaries that will need to be materialized later. +// The StackFrameContext assures that nested calls due to inlined recursive +// functions do not interfere. +REGISTER_TRAIT_WITH_PROGRAMSTATE(TemporaryMaterializations, + TemporaryMaterializationMap) + typedef llvm::ImmutableMap, SVal> CXXNewAllocatorValuesMap; // Keeps track of return values of various operator new() calls between // evaluation of the inlined operator new(), through the constructor call, // to the actual evaluation of the CXXNewExpr. // TODO: Refactor the key for this trait into a LocationContext sub-class, // which would be put on the stack of location contexts before operator new() // is evaluated, and removed from the stack when the whole CXXNewExpr // is fully evaluated. // Probably do something similar to the previous trait as well. REGISTER_TRAIT_WITH_PROGRAMSTATE(CXXNewAllocatorValues, CXXNewAllocatorValuesMap) //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// static const char* TagProviderName = "ExprEngine"; ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS, InliningModes HowToInlineIn) : AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), StateMgr(getContext(), mgr.getStoreManagerCreator(), mgr.getConstraintManagerCreator(), G.getAllocator(), this), SymMgr(StateMgr.getSymbolManager()), svalBuilder(StateMgr.getSValBuilder()), currStmtIdx(0), currBldrCtx(nullptr), ObjCNoRet(mgr.getASTContext()), ObjCGCEnabled(gcEnabled), BR(mgr, *this), VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.getGraphTrimInterval(); if (TrimInterval != 0) { // Enable eager node reclaimation when constructing the ExplodedGraph. G.enableNodeReclamation(TrimInterval); } } ExprEngine::~ExprEngine() { BR.FlushReports(); } //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { ProgramStateRef state = StateMgr.getInitialState(InitLoc); const Decl *D = InitLoc->getDecl(); // Preconditions. // FIXME: It would be nice if we had a more general mechanism to add // such preconditions. Some day. do { if (const FunctionDecl *FD = dyn_cast(D)) { // Precondition: the first argument of 'main' is an integer guaranteed // to be > 0. const IdentifierInfo *II = FD->getIdentifier(); if (!II || !(II->getName() == "main" && FD->getNumParams() > 0)) break; const ParmVarDecl *PD = FD->getParamDecl(0); QualType T = PD->getType(); const BuiltinType *BT = dyn_cast(T); if (!BT || !BT->isInteger()) break; const MemRegion *R = state->getRegion(PD, InitLoc); if (!R) break; SVal V = state->getSVal(loc::MemRegionVal(R)); SVal Constraint_untested = evalBinOp(state, BO_GT, V, svalBuilder.makeZeroVal(T), svalBuilder.getConditionType()); Optional Constraint = Constraint_untested.getAs(); if (!Constraint) break; if (ProgramStateRef newState = state->assume(*Constraint, true)) state = newState; } break; } while (0); if (const ObjCMethodDecl *MD = dyn_cast(D)) { // Precondition: 'self' is always non-null upon entry to an Objective-C // method. const ImplicitParamDecl *SelfD = MD->getSelfDecl(); const MemRegion *R = state->getRegion(SelfD, InitLoc); SVal V = state->getSVal(loc::MemRegionVal(R)); if (Optional LV = V.getAs()) { // Assume that the pointer value in 'self' is non-null. state = state->assume(*LV, true); assert(state && "'self' cannot be null"); } } if (const CXXMethodDecl *MD = dyn_cast(D)) { if (!MD->isStatic()) { // Precondition: 'this' is always non-null upon entry to the // top-level function. This is our starting assumption for // analyzing an "open" program. const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); if (SFC->getParent() == nullptr) { loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); if (Optional LV = V.getAs()) { state = state->assume(*LV, true); assert(state && "'this' cannot be null"); } } } } return state; } ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, const LocationContext *LC, const Expr *InitWithAdjustments, const Expr *Result) { // FIXME: This function is a hack that works around the quirky AST // we're often having with respect to C++ temporaries. If only we modelled // the actual execution order of statements properly in the CFG, // all the hassle with adjustments would not be necessary, // and perhaps the whole function would be removed. SVal InitValWithAdjustments = State->getSVal(InitWithAdjustments, LC); if (!Result) { // If we don't have an explicit result expression, we're in "if needed" // mode. Only create a region if the current value is a NonLoc. if (!InitValWithAdjustments.getAs()) return State; Result = InitWithAdjustments; } else { // We need to create a region no matter what. For sanity, make sure we don't // try to stuff a Loc into a non-pointer temporary region. assert(!InitValWithAdjustments.getAs() || Loc::isLocType(Result->getType()) || Result->getType()->isMemberPointerType()); } ProgramStateManager &StateMgr = State->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); StoreManager &StoreMgr = StateMgr.getStoreManager(); // MaterializeTemporaryExpr may appear out of place, after a few field and // base-class accesses have been made to the object, even though semantically // it is the whole object that gets materialized and lifetime-extended. // // For example: // // `-MaterializeTemporaryExpr // `-MemberExpr // `-CXXTemporaryObjectExpr // // instead of the more natural // // `-MemberExpr // `-MaterializeTemporaryExpr // `-CXXTemporaryObjectExpr // // Use the usual methods for obtaining the expression of the base object, // and record the adjustments that we need to make to obtain the sub-object // that the whole expression 'Ex' refers to. This trick is usual, // in the sense that CodeGen takes a similar route. SmallVector CommaLHSs; SmallVector Adjustments; const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments( CommaLHSs, Adjustments); + // Take the region for Init, i.e. for the whole object. If we do not remember + // the region in which the object originally was constructed, come up with + // a new temporary region out of thin air and copy the contents of the object + // (which are currently present in the Environment, because Init is an rvalue) + // into that region. This is not correct, but it is better than nothing. + bool FoundOriginalMaterializationRegion = false; const TypedValueRegion *TR = nullptr; if (const MaterializeTemporaryExpr *MT = dyn_cast(Result)) { - StorageDuration SD = MT->getStorageDuration(); - // If this object is bound to a reference with static storage duration, we - // put it in a different region to prevent "address leakage" warnings. - if (SD == SD_Static || SD == SD_Thread) - TR = MRMgr.getCXXStaticTempObjectRegion(Init); - } - if (!TR) + auto Key = std::make_pair(MT, LC->getCurrentStackFrame()); + if (const CXXTempObjectRegion *const *TRPtr = + State->get(Key)) { + FoundOriginalMaterializationRegion = true; + TR = *TRPtr; + assert(TR); + State = State->remove(Key); + } else { + StorageDuration SD = MT->getStorageDuration(); + // If this object is bound to a reference with static storage duration, we + // put it in a different region to prevent "address leakage" warnings. + if (SD == SD_Static || SD == SD_Thread) { + TR = MRMgr.getCXXStaticTempObjectRegion(Init); + } else { + TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } + } + } else { TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } SVal Reg = loc::MemRegionVal(TR); SVal BaseReg = Reg; // Make the necessary adjustments to obtain the sub-object. for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) { const SubobjectAdjustment &Adj = *I; switch (Adj.Kind) { case SubobjectAdjustment::DerivedToBaseAdjustment: Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath); break; case SubobjectAdjustment::FieldAdjustment: Reg = StoreMgr.getLValueField(Adj.Field, Reg); break; case SubobjectAdjustment::MemberPointerAdjustment: // FIXME: Unimplemented. State = State->bindDefault(Reg, UnknownVal(), LC); return State; } } - // What remains is to copy the value of the object to the new region. - // FIXME: In other words, what we should always do is copy value of the - // Init expression (which corresponds to the bigger object) to the whole - // temporary region TR. However, this value is often no longer present - // in the Environment. If it has disappeared, we instead invalidate TR. - // Still, what we can do is assign the value of expression Ex (which - // corresponds to the sub-object) to the TR's sub-region Reg. At least, - // values inside Reg would be correct. - SVal InitVal = State->getSVal(Init, LC); - if (InitVal.isUnknown()) { - InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(), - currBldrCtx->blockCount()); - State = State->bindLoc(BaseReg.castAs(), InitVal, LC, false); - - // Then we'd need to take the value that certainly exists and bind it over. - if (InitValWithAdjustments.isUnknown()) { - // Try to recover some path sensitivity in case we couldn't - // compute the value. - InitValWithAdjustments = getSValBuilder().conjureSymbolVal( - Result, LC, InitWithAdjustments->getType(), - currBldrCtx->blockCount()); + if (!FoundOriginalMaterializationRegion) { + // What remains is to copy the value of the object to the new region. + // FIXME: In other words, what we should always do is copy value of the + // Init expression (which corresponds to the bigger object) to the whole + // temporary region TR. However, this value is often no longer present + // in the Environment. If it has disappeared, we instead invalidate TR. + // Still, what we can do is assign the value of expression Ex (which + // corresponds to the sub-object) to the TR's sub-region Reg. At least, + // values inside Reg would be correct. + SVal InitVal = State->getSVal(Init, LC); + if (InitVal.isUnknown()) { + InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(), + currBldrCtx->blockCount()); + State = State->bindLoc(BaseReg.castAs(), InitVal, LC, false); + + // Then we'd need to take the value that certainly exists and bind it + // over. + if (InitValWithAdjustments.isUnknown()) { + // Try to recover some path sensitivity in case we couldn't + // compute the value. + InitValWithAdjustments = getSValBuilder().conjureSymbolVal( + Result, LC, InitWithAdjustments->getType(), + currBldrCtx->blockCount()); + } + State = + State->bindLoc(Reg.castAs(), InitValWithAdjustments, LC, false); + } else { + State = State->bindLoc(BaseReg.castAs(), InitVal, LC, false); } - State = - State->bindLoc(Reg.castAs(), InitValWithAdjustments, LC, false); - } else { - State = State->bindLoc(BaseReg.castAs(), InitVal, LC, false); } // The result expression would now point to the correct sub-region of the // newly created temporary region. Do this last in order to getSVal of Init // correctly in case (Result == Init). State = State->BindExpr(Result, LC, Reg); - // Notify checkers once for two bindLoc()s. - State = processRegionChange(State, TR, LC); + if (!FoundOriginalMaterializationRegion) { + // Notify checkers once for two bindLoc()s. + State = processRegionChange(State, TR, LC); + } return State; } ProgramStateRef ExprEngine::addInitializedTemporary( ProgramStateRef State, const CXXBindTemporaryExpr *BTE, const LocationContext *LC, const CXXTempObjectRegion *R) { const auto &Key = std::make_pair(BTE, LC->getCurrentStackFrame()); if (!State->contains(Key)) { return State->set(Key, R); } // FIXME: Currently the state might already contain the marker due to // incorrect handling of temporaries bound to default parameters; for // those, we currently skip the CXXBindTemporaryExpr but rely on adding // temporary destructor nodes. Otherwise, this branch should be unreachable. return State; } bool ExprEngine::areInitializedTemporariesClear(ProgramStateRef State, const LocationContext *FromLC, const LocationContext *ToLC) { const LocationContext *LC = FromLC; while (LC != ToLC) { assert(LC && "ToLC must be a parent of FromLC!"); for (auto I : State->get()) if (I.first.second == LC) return false; LC = LC->getParent(); } return true; } +ProgramStateRef ExprEngine::addTemporaryMaterialization( + ProgramStateRef State, const MaterializeTemporaryExpr *MTE, + const LocationContext *LC, const CXXTempObjectRegion *R) { + const auto &Key = std::make_pair(MTE, LC->getCurrentStackFrame()); + assert(!State->contains(Key)); + return State->set(Key, R); +} + +bool ExprEngine::areTemporaryMaterializationsClear( + ProgramStateRef State, const LocationContext *FromLC, + const LocationContext *ToLC) { + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get()) + if (I.first.second == LC) + return false; + + LC = LC->getParent(); + } + return true; +} + ProgramStateRef ExprEngine::setCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC, SVal V) { assert(!State->get(std::make_pair(CNE, CallerLC)) && "Allocator value already set!"); return State->set(std::make_pair(CNE, CallerLC), V); } SVal ExprEngine::getCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC) { return *State->get(std::make_pair(CNE, CallerLC)); } ProgramStateRef ExprEngine::clearCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC) { return State->remove(std::make_pair(CNE, CallerLC)); } bool ExprEngine::areCXXNewAllocatorValuesClear(ProgramStateRef State, const LocationContext *FromLC, const LocationContext *ToLC) { const LocationContext *LC = FromLC; while (LC != ToLC) { assert(LC && "ToLC must be a parent of FromLC!"); for (auto I : State->get()) if (I.first.second == LC) return false; LC = LC->getParent(); } return true; } //===----------------------------------------------------------------------===// // Top-level transfer function logic (Dispatcher). //===----------------------------------------------------------------------===// /// evalAssume - Called by ConstraintManager. Used to call checker-specific /// logic for handling assumptions on symbolic values. ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, SVal cond, bool assumption) { return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); } ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef Explicits, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, Explicits, Regions, LCtx, Call); } static void printInitializedTemporariesForContext(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep, const LocationContext *LC) { PrintingPolicy PP = LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); for (auto I : State->get()) { std::pair Key = I.first; const MemRegion *Value = I.second; if (Key.second != LC) continue; Out << '(' << Key.second << ',' << Key.first << ") "; Key.first->printPretty(Out, nullptr, PP); if (Value) Out << " : " << Value; Out << NL; } } +static void printTemporaryMaterializationsForContext( + raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep, + const LocationContext *LC) { + PrintingPolicy PP = + LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + for (auto I : State->get()) { + std::pair Key = + I.first; + const MemRegion *Value = I.second; + if (Key.second != LC) + continue; + Out << '(' << Key.second << ',' << Key.first << ") "; + Key.first->printPretty(Out, nullptr, PP); + assert(Value); + Out << " : " << Value << NL; + } +} + static void printCXXNewAllocatorValuesForContext(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep, const LocationContext *LC) { PrintingPolicy PP = LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); for (auto I : State->get()) { std::pair Key = I.first; SVal Value = I.second; if (Key.second != LC) continue; Out << '(' << Key.second << ',' << Key.first << ") "; Key.first->printPretty(Out, nullptr, PP); Out << " : " << Value << NL; } } void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep, const LocationContext *LCtx) { if (LCtx) { if (!State->get().isEmpty()) { Out << Sep << "Initialized temporaries:" << NL; LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { printInitializedTemporariesForContext(Out, State, NL, Sep, LC); }); } + if (!State->get().isEmpty()) { + Out << Sep << "Temporaries to be materialized:" << NL; + + LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + printTemporaryMaterializationsForContext(Out, State, NL, Sep, LC); + }); + } + if (!State->get().isEmpty()) { Out << Sep << "operator new() allocator return values:" << NL; LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { printCXXNewAllocatorValuesForContext(Out, State, NL, Sep, LC); }); } } getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } void ExprEngine::processEndWorklist(bool hasWorkRemaining) { getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); } void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, unsigned StmtIdx, NodeBuilderContext *Ctx) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); currStmtIdx = StmtIdx; currBldrCtx = Ctx; switch (E.getKind()) { case CFGElement::Statement: case CFGElement::Constructor: ProcessStmt(E.castAs().getStmt(), Pred); return; case CFGElement::Initializer: ProcessInitializer(E.castAs(), Pred); return; case CFGElement::NewAllocator: ProcessNewAllocator(E.castAs().getAllocatorExpr(), Pred); return; case CFGElement::AutomaticObjectDtor: case CFGElement::DeleteDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: ProcessImplicitDtor(E.castAs(), Pred); return; case CFGElement::LoopExit: ProcessLoopExit(E.castAs().getLoopStmt(), Pred); return; case CFGElement::LifetimeEnds: return; } } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, const Stmt *S, const ExplodedNode *Pred, const LocationContext *LC) { // Are we never purging state values? if (AMgr.options.AnalysisPurgeOpt == PurgeNone) return false; // Is this the beginning of a basic block? if (Pred->getLocation().getAs()) return true; // Is this on a non-expression? if (!isa(S)) return true; // Run before processing a call. if (CallEvent::isCallStmt(S)) return true; // Is this an expression that is consumed by another expression? If so, // postpone cleaning out the state. ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); return !PM.isConsumedExpr(cast(S)); } void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, const Stmt *ReferenceStmt, const LocationContext *LC, const Stmt *DiagnosticStmt, ProgramPoint::Kind K) { assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || ReferenceStmt == nullptr || isa(ReferenceStmt)) && "PostStmt is not generally supported by the SymbolReaper yet"); assert(LC && "Must pass the current (or expiring) LocationContext"); if (!DiagnosticStmt) { DiagnosticStmt = ReferenceStmt; assert(DiagnosticStmt && "Required for clearing a LocationContext"); } NumRemoveDeadBindings++; ProgramStateRef CleanedState = Pred->getState(); // LC is the location context being destroyed, but SymbolReaper wants a // location context that is still live. (If this is the top-level stack // frame, this will be null.) if (!ReferenceStmt) { assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind && "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext"); LC = LC->getParent(); } const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : nullptr; SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); for (auto I : CleanedState->get()) if (I.second) SymReaper.markLive(I.second); + for (auto I : CleanedState->get()) + if (I.second) + SymReaper.markLive(I.second); + for (auto I : CleanedState->get()) { if (SymbolRef Sym = I.second.getAsSymbol()) SymReaper.markLive(Sym); if (const MemRegion *MR = I.second.getAsRegion()) SymReaper.markElementIndicesLive(MR); } getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); // Create a state in which dead bindings are removed from the environment // and the store. TODO: The function should just return new env and store, // not a new state. CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); // Process any special transfer function for dead symbols. // A tag to track convenience transitions, which can be removed at cleanup. static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node"); if (!SymReaper.hasDeadSymbols()) { // Generate a CleanedNode that has the environment and store cleaned // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. StmtNodeBuilder Bldr(Pred, Out, *currBldrCtx); Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, &cleanupTag, K); } else { // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, DiagnosticStmt, *this, K); // For each node in CheckedSet, generate CleanedNodes that have the // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); for (ExplodedNodeSet::const_iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { ProgramStateRef CheckerState = (*I)->getState(); // The constraint manager has not been cleaned up yet, so clean up now. CheckerState = getConstraintManager().removeDeadBindings(CheckerState, SymReaper); assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Environment as a part of " "checkDeadSymbols processing."); assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Store as a part of " "checkDeadSymbols processing."); // Create a state based on CleanedState with CheckerState GDM and // generate a transition to that state. ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, &cleanupTag, K); } } } void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. ExplodedNodeSet CleanedStates; if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, Pred->getLocationContext())) { removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); } else CleanedStates.Add(Pred); // Visit the statement. ExplodedNodeSet Dst; for (ExplodedNodeSet::iterator I = CleanedStates.begin(), E = CleanedStates.end(); I != E; ++I) { ExplodedNodeSet DstI; // Visit the statement. Visit(currStmt, *I, DstI); Dst.insert(DstI); } // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating end of the loop"); ExplodedNodeSet Dst; Dst.Add(Pred); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef NewState = Pred->getState(); if(AMgr.options.shouldUnrollLoops()) NewState = processLoopEnd(S, NewState); LoopExit PP(S, Pred->getLocationContext()); Bldr.generateNode(PP, NewState, Pred); // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } void ExprEngine::ProcessInitializer(const CFGInitializer Init, ExplodedNode *Pred) { const CXXCtorInitializer *BMI = Init.getInitializer(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), BMI->getSourceLocation(), "Error evaluating initializer"); // We don't clean up dead bindings here. const StackFrameContext *stackFrame = cast(Pred->getLocationContext()); const CXXConstructorDecl *decl = cast(stackFrame->getDecl()); ProgramStateRef State = Pred->getState(); SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); ExplodedNodeSet Tmp(Pred); SVal FieldLoc; // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { // Constructors build the object directly in the field, // but non-objects must be copied in from the initializer. if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) { assert(BMI->getInit()->IgnoreImplicit() == CtorExpr); (void)CtorExpr; // The field was directly constructed, so there is no need to bind. } else { const Expr *Init = BMI->getInit()->IgnoreImplicit(); const ValueDecl *Field; if (BMI->isIndirectMemberInitializer()) { Field = BMI->getIndirectMember(); FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); } else { Field = BMI->getMember(); FieldLoc = State->getLValue(BMI->getMember(), thisVal); } SVal InitVal; if (Init->getType()->isArrayType()) { // Handle arrays of trivial type. We can represent this with a // primitive load/copy from the base array region. const ArraySubscriptExpr *ASE; while ((ASE = dyn_cast(Init))) Init = ASE->getBase()->IgnoreImplicit(); SVal LValue = State->getSVal(Init, stackFrame); if (!Field->getType()->isReferenceType()) if (Optional LValueLoc = LValue.getAs()) InitVal = State->getSVal(*LValueLoc); // If we fail to get the value for some reason, use a symbolic value. if (InitVal.isUnknownOrUndef()) { SValBuilder &SVB = getSValBuilder(); InitVal = SVB.conjureSymbolVal(BMI->getInit(), stackFrame, Field->getType(), currBldrCtx->blockCount()); } } else { InitVal = State->getSVal(BMI->getInit(), stackFrame); } assert(Tmp.size() == 1 && "have not generated any new nodes yet"); assert(*Tmp.begin() == Pred && "have not generated any new nodes yet"); Tmp.clear(); PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); } } else { assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); // We already did all the work when visiting the CXXConstructExpr. } // Construct PostInitializer nodes whether the state changed or not, // so that the diagnostics don't get confused. PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); ExplodedNodeSet Dst; NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { ExplodedNode *N = *I; Bldr.generateNode(PP, N->getState(), N); } // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred) { ExplodedNodeSet Dst; switch (D.getKind()) { case CFGElement::AutomaticObjectDtor: ProcessAutomaticObjDtor(D.castAs(), Pred, Dst); break; case CFGElement::BaseDtor: ProcessBaseDtor(D.castAs(), Pred, Dst); break; case CFGElement::MemberDtor: ProcessMemberDtor(D.castAs(), Pred, Dst); break; case CFGElement::TemporaryDtor: ProcessTemporaryDtor(D.castAs(), Pred, Dst); break; case CFGElement::DeleteDtor: ProcessDeleteDtor(D.castAs(), Pred, Dst); break; default: llvm_unreachable("Unexpected dtor kind."); } // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, ExplodedNode *Pred) { ExplodedNodeSet Dst; AnalysisManager &AMgr = getAnalysisManager(); AnalyzerOptions &Opts = AMgr.options; // TODO: We're not evaluating allocators for all cases just yet as // we're not handling the return value correctly, which causes false // positives when the alpha.cplusplus.NewDeleteLeaks check is on. if (Opts.mayInlineCXXAllocator()) VisitCXXNewAllocatorCall(NE, Pred, Dst); else { NodeBuilder Bldr(Pred, Dst, *currBldrCtx); const LocationContext *LCtx = Pred->getLocationContext(); PostImplicitCall PP(NE->getOperatorNew(), NE->getLocStart(), LCtx); Bldr.generateNode(PP, Pred->getState(), Pred); } Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const VarDecl *varDecl = Dtor.getVarDecl(); QualType varType = varDecl->getType(); ProgramStateRef state = Pred->getState(); SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); const MemRegion *Region = dest.castAs().getRegion(); if (varType->isReferenceType()) { const MemRegion *ValueRegion = state->getSVal(Region).getAsRegion(); if (!ValueRegion) { // FIXME: This should not happen. The language guarantees a presence // of a valid initializer here, so the reference shall not be undefined. // It seems that we're calling destructors over variables that // were not initialized yet. return; } Region = ValueRegion->getBaseRegion(); varType = cast(Region)->getValueType(); } // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still // invalidate the entire array). EvalCallOptions CallOpts; Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType, CallOpts.IsArrayCtorOrDtor).getAsRegion(); VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, Pred, Dst, CallOpts); } void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); const CXXDeleteExpr *DE = Dtor.getDeleteExpr(); const Stmt *Arg = DE->getArgument(); QualType DTy = DE->getDestroyedType(); SVal ArgVal = State->getSVal(Arg, LCtx); // If the argument to delete is known to be a null value, // don't run destructor. if (State->isNull(ArgVal).isConstrainedTrue()) { QualType BTy = getContext().getBaseElementType(DTy); const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); const CXXDestructorDecl *Dtor = RD->getDestructor(); PostImplicitCall PP(Dtor, DE->getLocStart(), LCtx); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; } EvalCallOptions CallOpts; const MemRegion *ArgR = ArgVal.getAsRegion(); if (DE->isArrayForm()) { // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still // invalidate the entire array). CallOpts.IsArrayCtorOrDtor = true; if (ArgR) ArgR = getStoreManager().GetElementZeroRegion(cast(ArgR), DTy); } VisitCXXDestructor(DE->getDestroyedType(), ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LCtx = Pred->getLocationContext(); const CXXDestructorDecl *CurDtor = cast(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, LCtx->getCurrentStackFrame()); SVal ThisVal = Pred->getState()->getSVal(ThisPtr); // Create the base object region. const CXXBaseSpecifier *Base = D.getBaseSpecifier(); QualType BaseTy = Base->getType(); SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, Base->isVirtual()); VisitCXXDestructor(BaseTy, BaseVal.castAs().getRegion(), CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {}); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const FieldDecl *Member = D.getFieldDecl(); QualType T = Member->getType(); ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); const CXXDestructorDecl *CurDtor = cast(LCtx->getDecl()); Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, LCtx->getCurrentStackFrame()); SVal FieldVal = State->getLValue(Member, State->getSVal(ThisVal).castAs()); // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still // invalidate the entire array). EvalCallOptions CallOpts; FieldVal = makeZeroElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor); VisitCXXDestructor(T, FieldVal.castAs().getRegion(), CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ExplodedNodeSet CleanDtorState; StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); ProgramStateRef State = Pred->getState(); const MemRegion *MR = nullptr; if (const CXXTempObjectRegion *const *MRPtr = State->get(std::make_pair( D.getBindTemporaryExpr(), Pred->getStackFrame()))) { // FIXME: Currently we insert temporary destructors for default parameters, // but we don't insert the constructors, so the entry in // InitializedTemporaries may be missing. State = State->remove( std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())); // *MRPtr may still be null when the construction context for the temporary // was not implemented. MR = *MRPtr; } StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType(); // FIXME: Currently CleanDtorState can be empty here due to temporaries being // bound to default parameters. assert(CleanDtorState.size() <= 1); ExplodedNode *CleanPred = CleanDtorState.empty() ? Pred : *CleanDtorState.begin(); EvalCallOptions CallOpts; CallOpts.IsTemporaryCtorOrDtor = true; if (!MR) { CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; // If we have no MR, we still need to unwrap the array to avoid destroying // the whole array at once. Regardless, we'd eventually need to model array // destructors properly, element-by-element. while (const ArrayType *AT = getContext().getAsArrayType(T)) { T = AT->getElementType(); CallOpts.IsArrayCtorOrDtor = true; } } else { // We'd eventually need to makeZeroElementRegion() trick here, // but for now we don't have the respective construction contexts, // so MR would always be null in this case. Do nothing for now. } VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(), /*IsBase=*/false, CleanPred, Dst, CallOpts); } void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, NodeBuilderContext &BldCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF); if (Pred->getState()->contains( std::make_pair(BTE, Pred->getStackFrame()))) { TempDtorBuilder.markInfeasible(false); TempDtorBuilder.generateNode(Pred->getState(), true, Pred); } else { TempDtorBuilder.markInfeasible(true); TempDtorBuilder.generateNode(Pred->getState(), false, Pred); } } namespace { class CollectReachableSymbolsCallback final : public SymbolVisitor { InvalidatedSymbols Symbols; public: explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} const InvalidatedSymbols &getSymbols() const { return Symbols; } bool VisitSymbol(SymbolRef Sym) override { Symbols.insert(Sym); return true; } }; } // end anonymous namespace void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating statement"); ExplodedNodeSet Dst; StmtNodeBuilder Bldr(Pred, DstTop, *currBldrCtx); assert(!isa(S) || S == cast(S)->IgnoreParens()); switch (S->getStmtClass()) { // C++, OpenMP and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXInheritedCtorInitExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: case Stmt::CXXFoldExprClass: case Stmt::MSPropertyRefExprClass: case Stmt::MSPropertySubscriptExprClass: case Stmt::CXXUnresolvedConstructExprClass: case Stmt::DependentScopeDeclRefExprClass: case Stmt::ArrayTypeTraitExprClass: case Stmt::ExpressionTraitExprClass: case Stmt::UnresolvedLookupExprClass: case Stmt::UnresolvedMemberExprClass: case Stmt::TypoExprClass: case Stmt::CXXNoexceptExprClass: case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::FunctionParmPackExprClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CoawaitExprClass: case Stmt::DependentCoawaitExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: case Stmt::SEHFinallyStmtClass: case Stmt::OMPParallelDirectiveClass: case Stmt::OMPSimdDirectiveClass: case Stmt::OMPForDirectiveClass: case Stmt::OMPForSimdDirectiveClass: case Stmt::OMPSectionsDirectiveClass: case Stmt::OMPSectionDirectiveClass: case Stmt::OMPSingleDirectiveClass: case Stmt::OMPMasterDirectiveClass: case Stmt::OMPCriticalDirectiveClass: case Stmt::OMPParallelForDirectiveClass: case Stmt::OMPParallelForSimdDirectiveClass: case Stmt::OMPParallelSectionsDirectiveClass: case Stmt::OMPTaskDirectiveClass: case Stmt::OMPTaskyieldDirectiveClass: case Stmt::OMPBarrierDirectiveClass: case Stmt::OMPTaskwaitDirectiveClass: case Stmt::OMPTaskgroupDirectiveClass: case Stmt::OMPFlushDirectiveClass: case Stmt::OMPOrderedDirectiveClass: case Stmt::OMPAtomicDirectiveClass: case Stmt::OMPTargetDirectiveClass: case Stmt::OMPTargetDataDirectiveClass: case Stmt::OMPTargetEnterDataDirectiveClass: case Stmt::OMPTargetExitDataDirectiveClass: case Stmt::OMPTargetParallelDirectiveClass: case Stmt::OMPTargetParallelForDirectiveClass: case Stmt::OMPTargetUpdateDirectiveClass: case Stmt::OMPTeamsDirectiveClass: case Stmt::OMPCancellationPointDirectiveClass: case Stmt::OMPCancelDirectiveClass: case Stmt::OMPTaskLoopDirectiveClass: case Stmt::OMPTaskLoopSimdDirectiveClass: case Stmt::OMPDistributeDirectiveClass: case Stmt::OMPDistributeParallelForDirectiveClass: case Stmt::OMPDistributeParallelForSimdDirectiveClass: case Stmt::OMPDistributeSimdDirectiveClass: case Stmt::OMPTargetParallelForSimdDirectiveClass: case Stmt::OMPTargetSimdDirectiveClass: case Stmt::OMPTeamsDistributeDirectiveClass: case Stmt::OMPTeamsDistributeSimdDirectiveClass: case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTeamsDistributeParallelForDirectiveClass: case Stmt::OMPTargetTeamsDirectiveClass: case Stmt::OMPTargetTeamsDistributeDirectiveClass: case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: case Stmt::CapturedStmtClass: { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); break; } case Stmt::ParenExprClass: llvm_unreachable("ParenExprs already handled."); case Stmt::GenericSelectionExprClass: llvm_unreachable("GenericSelectionExprs already handled."); // Cases that should never be evaluated simply because they shouldn't // appear in the CFG. case Stmt::BreakStmtClass: case Stmt::CaseStmtClass: case Stmt::CompoundStmtClass: case Stmt::ContinueStmtClass: case Stmt::CXXForRangeStmtClass: case Stmt::DefaultStmtClass: case Stmt::DoStmtClass: case Stmt::ForStmtClass: case Stmt::GotoStmtClass: case Stmt::IfStmtClass: case Stmt::IndirectGotoStmtClass: case Stmt::LabelStmtClass: case Stmt::NoStmtClass: case Stmt::NullStmtClass: case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: case Expr::MSDependentExistsStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: case Stmt::ObjCPropertyRefExprClass: llvm_unreachable("These are handled by PseudoObjectExpr"); case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. ProgramStateRef state = Pred->getState(); state = state->BindExpr(S, Pred->getLocationContext(), svalBuilder.makeIntValWithPtrWidth(0, false)); Bldr.generateNode(S, Pred, state); break; } case Stmt::ObjCAtSynchronizedStmtClass: Bldr.takeNodes(Pred); VisitObjCAtSynchronizedStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::ExprWithCleanupsClass: // Handled due to fully linearised CFG. break; case Stmt::CXXBindTemporaryExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); getCheckerManager().runCheckersForPostStmt(Dst, PreVisit, S, *this); Bldr.addNodes(Dst); break; } // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::DesignatedInitUpdateExprClass: case Stmt::ArrayInitLoopExprClass: case Stmt::ArrayInitIndexExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: case Stmt::ObjCAtCatchStmtClass: case Stmt::ObjCAtFinallyStmtClass: case Stmt::ObjCAtTryStmtClass: case Stmt::ObjCAutoreleasePoolStmtClass: case Stmt::ObjCEncodeExprClass: case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: case Stmt::ParenListExprClass: case Stmt::ShuffleVectorExprClass: case Stmt::ConvertVectorExprClass: case Stmt::VAArgExprClass: case Stmt::CUDAKernelCallExprClass: case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need // to be explicitly evaluated. case Stmt::PredefinedExprClass: case Stmt::AddrLabelExprClass: case Stmt::AttributedStmtClass: case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::ImplicitValueInitExprClass: case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXBoolLiteralExprClass: case Stmt::ObjCBoolLiteralExprClass: case Stmt::ObjCAvailabilityCheckExprClass: case Stmt::FloatingLiteralClass: case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: case Stmt::OMPArraySectionExprClass: case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this); Bldr.addNodes(Dst); break; } case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); const Expr *ArgE; if (const CXXDefaultArgExpr *DefE = dyn_cast(S)) ArgE = DefE->getExpr(); else if (const CXXDefaultInitExpr *DefE = dyn_cast(S)) ArgE = DefE->getExpr(); else llvm_unreachable("unknown constant wrapper kind"); bool IsTemporary = false; if (const MaterializeTemporaryExpr *MTE = dyn_cast(ArgE)) { ArgE = MTE->GetTemporaryExpr(); IsTemporary = true; } Optional ConstantVal = svalBuilder.getConstantVal(ArgE); if (!ConstantVal) ConstantVal = UnknownVal(); const LocationContext *LCtx = Pred->getLocationContext(); for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); State = State->BindExpr(S, LCtx, *ConstantVal); if (IsTemporary) State = createTemporaryRegionIfNeeded(State, LCtx, cast(S), cast(S)); Bldr2.generateNode(S, *I, State); } getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); Bldr.addNodes(Dst); break; } // Cases we evaluate as opaque expressions, conjuring a symbol. case Stmt::CXXStdInitializerListExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoxedExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx); const Expr *Ex = cast(S); QualType resultType = Ex->getType(); for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); it != et; ++it) { ExplodedNode *N = *it; const LocationContext *LCtx = N->getLocationContext(); SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, resultType, currBldrCtx->blockCount()); ProgramStateRef State = N->getState()->BindExpr(Ex, LCtx, result); // Escape pointers passed into the list, unless it's an ObjC boxed // expression which is not a boxable C structure. if (!(isa(Ex) && !cast(Ex)->getSubExpr() ->getType()->isRecordType())) for (auto Child : Ex->children()) { assert(Child); SVal Val = State->getSVal(Child, LCtx); CollectReachableSymbolsCallback Scanner = State->scanReachableSymbols( Val); const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); State = getCheckerManager().runCheckersForPointerEscape( State, EscapedSymbols, /*CallEvent*/ nullptr, PSK_EscapeOther, nullptr); } Bldr2.generateNode(S, N, State); } getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); Bldr.addNodes(Dst); break; } case Stmt::ArraySubscriptExprClass: Bldr.takeNodes(Pred); VisitArraySubscriptExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::GCCAsmStmtClass: Bldr.takeNodes(Pred); VisitGCCAsmStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::MSAsmStmtClass: Bldr.takeNodes(Pred); VisitMSAsmStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::BlockExprClass: Bldr.takeNodes(Pred); VisitBlockExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::LambdaExprClass: if (AMgr.options.shouldInlineLambdas()) { Bldr.takeNodes(Pred); VisitLambdaExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); } else { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); } break; case Stmt::BinaryOperatorClass: { const BinaryOperator* B = cast(S); if (B->isLogicalOp()) { Bldr.takeNodes(Pred); VisitLogicalExpr(B, Pred, Dst); Bldr.addNodes(Dst); break; } else if (B->getOpcode() == BO_Comma) { ProgramStateRef state = Pred->getState(); Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), state->getSVal(B->getRHS(), Pred->getLocationContext()))); break; } Bldr.takeNodes(Pred); if (AMgr.options.eagerlyAssumeBinOpBifurcation && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; VisitBinaryOperator(cast(S), Pred, Tmp); evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, cast(S)); } else VisitBinaryOperator(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::CXXOperatorCallExprClass: { const CXXOperatorCallExpr *OCE = cast(S); // For instance method operators, make sure the 'this' argument has a // valid region. const Decl *Callee = OCE->getCalleeDecl(); if (const CXXMethodDecl *MD = dyn_cast_or_null(Callee)) { if (MD->isInstance()) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef NewState = createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0)); if (NewState != State) { Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr, ProgramPoint::PreStmtKind); // Did we cache out? if (!Pred) break; } } } // FALLTHROUGH LLVM_FALLTHROUGH; } case Stmt::CallExprClass: case Stmt::CXXMemberCallExprClass: case Stmt::UserDefinedLiteralClass: { Bldr.takeNodes(Pred); VisitCallExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::CXXCatchStmtClass: { Bldr.takeNodes(Pred); VisitCXXCatchStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::CXXTemporaryObjectExprClass: case Stmt::CXXConstructExprClass: { Bldr.takeNodes(Pred); VisitCXXConstructExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::CXXNewExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); ExplodedNodeSet PostVisit; for (ExplodedNodeSet::iterator i = PreVisit.begin(), e = PreVisit.end(); i != e ; ++i) { VisitCXXNewExpr(cast(S), *i, PostVisit); } getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; } case Stmt::CXXDeleteExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; const CXXDeleteExpr *CDE = cast(S); getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); for (ExplodedNodeSet::iterator i = PreVisit.begin(), e = PreVisit.end(); i != e ; ++i) VisitCXXDeleteExpr(CDE, *i, Dst); Bldr.addNodes(Dst); break; } // FIXME: ChooseExpr is really a constant. We need to fix // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr Bldr.takeNodes(Pred); const ChooseExpr *C = cast(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::CompoundAssignOperatorClass: Bldr.takeNodes(Pred); VisitBinaryOperator(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::CompoundLiteralExprClass: Bldr.takeNodes(Pred); VisitCompoundLiteralExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // '?' operator Bldr.takeNodes(Pred); const AbstractConditionalOperator *C = cast(S); VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::CXXThisExprClass: Bldr.takeNodes(Pred); VisitCXXThisExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::DeclRefExprClass: { Bldr.takeNodes(Pred); const DeclRefExpr *DE = cast(S); VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::DeclStmtClass: Bldr.takeNodes(Pred); VisitDeclStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: case Stmt::CXXDynamicCastExprClass: case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const CastExpr *C = cast(S); ExplodedNodeSet dstExpr; VisitCast(C, C->getSubExpr(), Pred, dstExpr); // Handle the postvisit checks. getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); Bldr.addNodes(Dst); break; } case Expr::MaterializeTemporaryExprClass: { Bldr.takeNodes(Pred); const MaterializeTemporaryExpr *MTE = cast(S); ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this); ExplodedNodeSet dstExpr; for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), e = dstPrevisit.end(); i != e ; ++i) { CreateCXXTemporaryObject(MTE, *i, dstExpr); } getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this); Bldr.addNodes(Dst); break; } case Stmt::InitListExprClass: Bldr.takeNodes(Pred); VisitInitListExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::MemberExprClass: Bldr.takeNodes(Pred); VisitMemberExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::AtomicExprClass: Bldr.takeNodes(Pred); VisitAtomicExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::ObjCIvarRefExprClass: Bldr.takeNodes(Pred); VisitLvalObjCIvarRefExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::ObjCForCollectionStmtClass: Bldr.takeNodes(Pred); VisitObjCForCollectionStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::ObjCMessageExprClass: Bldr.takeNodes(Pred); VisitObjCMessage(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::ObjCAtThrowStmtClass: case Stmt::CXXThrowExprClass: // FIXME: This is not complete. We basically treat @throw as // an abort. Bldr.generateSink(S, Pred, Pred->getState()); break; case Stmt::ReturnStmtClass: Bldr.takeNodes(Pred); VisitReturnStmt(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::OffsetOfExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); ExplodedNodeSet PostVisit; for (ExplodedNode *Node : PreVisit) VisitOffsetOfExpr(cast(S), Node, PostVisit); getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; } case Stmt::UnaryExprOrTypeTraitExprClass: Bldr.takeNodes(Pred); VisitUnaryExprOrTypeTraitExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); break; case Stmt::StmtExprClass: { const StmtExpr *SE = cast(S); if (SE->getSubStmt()->body_empty()) { // Empty statement expression. assert(SE->getType() == getContext().VoidTy && "Empty statement expression must have void type."); break; } if (Expr *LastExpr = dyn_cast(*SE->getSubStmt()->body_rbegin())) { ProgramStateRef state = Pred->getState(); Bldr.generateNode(SE, Pred, state->BindExpr(SE, Pred->getLocationContext(), state->getSVal(LastExpr, Pred->getLocationContext()))); } break; } case Stmt::UnaryOperatorClass: { Bldr.takeNodes(Pred); const UnaryOperator *U = cast(S); if (AMgr.options.eagerlyAssumeBinOpBifurcation && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, U); } else VisitUnaryOperator(U, Pred, Dst); Bldr.addNodes(Dst); break; } case Stmt::PseudoObjectExprClass: { Bldr.takeNodes(Pred); ProgramStateRef state = Pred->getState(); const PseudoObjectExpr *PE = cast(S); if (const Expr *Result = PE->getResultExpr()) { SVal V = state->getSVal(Result, Pred->getLocationContext()); Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), V)); } else Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), UnknownVal())); Bldr.addNodes(Dst); break; } } } bool ExprEngine::replayWithoutInlining(ExplodedNode *N, const LocationContext *CalleeLC) { const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame(); const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); assert(CalleeSF && CallerSF); ExplodedNode *BeforeProcessingCall = nullptr; const Stmt *CE = CalleeSF->getCallSite(); // Find the first node before we started processing the call expression. while (N) { ProgramPoint L = N->getLocation(); BeforeProcessingCall = N; N = N->pred_empty() ? nullptr : *(N->pred_begin()); // Skip the nodes corresponding to the inlined code. if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) continue; // We reached the caller. Find the node right before we started // processing the call. if (L.isPurgeKind()) continue; if (L.getAs()) continue; if (L.getAs()) continue; if (Optional SP = L.getAs()) if (SP->getStmt() == CE) continue; break; } if (!BeforeProcessingCall) return false; // TODO: Clean up the unneeded nodes. // Build an Epsilon node from which we will restart the analyzes. // Note that CE is permitted to be NULL! ProgramPoint NewNodeLoc = EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); // Add the special flag to GDM to signal retrying with no inlining. // Note, changing the state ensures that we are not going to cache out. ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); NewNodeState = NewNodeState->set(const_cast(CE)); // Make the new node a successor of BeforeProcessingCall. bool IsNew = false; ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew); // We cached out at this point. Caching out is common due to us backtracking // from the inlined function, which might spawn several paths. if (!IsNew) return true; NewNode->addPredecessor(BeforeProcessingCall, G); // Add the new node to the work list. Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(), CalleeSF->getIndex()); NumTimesRetriedWithoutInlining++; return true; } /// Block entrance. (Update counters). void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, NodeBuilderWithSinks &nodeBuilder, ExplodedNode *Pred) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); // If we reach a loop which has a known bound (and meets // other constraints) then consider completely unrolling it. if(AMgr.options.shouldUnrollLoops()) { unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); if (Term) { ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(), Pred, maxBlockVisitOnPath); if (NewState != Pred->getState()) { ExplodedNode *UpdatedNode = nodeBuilder.generateNode(NewState, Pred); if (!UpdatedNode) return; Pred = UpdatedNode; } } // Is we are inside an unrolled loop then no need the check the counters. if(isUnrolledState(Pred->getState())) return; } // If this block is terminated by a loop and it has already been visited the // maximum number of times, widen the loop. unsigned int BlockCount = nodeBuilder.getContext().blockCount(); if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && AMgr.options.shouldWidenLoops()) { const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); if (!(Term && (isa(Term) || isa(Term) || isa(Term)))) return; // Widen. const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef WidenedState = getWidenedLoopState(Pred->getState(), LCtx, BlockCount, Term); nodeBuilder.generateNode(WidenedState, Pred); return; } // FIXME: Refactor this into a checker. if (BlockCount >= AMgr.options.maxBlockVisitOnPath) { static SimpleProgramPointTag tag(TagProviderName, "Block count exceeded"); const ExplodedNode *Sink = nodeBuilder.generateSink(Pred->getState(), Pred, &tag); // Check if we stopped at the top level function or not. // Root node should have the location context of the top most function. const LocationContext *CalleeLC = Pred->getLocation().getLocationContext(); const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame(); const LocationContext *RootLC = (*G.roots_begin())->getLocation().getLocationContext(); if (RootLC->getCurrentStackFrame() != CalleeSF) { Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); // Re-run the call evaluation without inlining it, by storing the // no-inlining policy in the state and enqueuing the new work item on // the list. Replay should almost never fail. Use the stats to catch it // if it does. if ((!AMgr.options.NoRetryExhausted && replayWithoutInlining(Pred, CalleeLC))) return; NumMaxBlockCountReachedInInlined++; } else NumMaxBlockCountReached++; // Make sink nodes as exhausted(for stats) only if retry failed. Engine.blocksExhausted.push_back(std::make_pair(L, Sink)); } } //===----------------------------------------------------------------------===// // Branch processing. //===----------------------------------------------------------------------===// /// RecoverCastedSymbol - A helper function for ProcessBranch that is used /// to try to recover some path-sensitivity for casts of symbolic /// integers that promote their values (which are currently not tracked well). /// This function returns the SVal bound to Condition->IgnoreCasts if all the // cast(s) did was sign-extend the original value. static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, ProgramStateRef state, const Stmt *Condition, const LocationContext *LCtx, ASTContext &Ctx) { const Expr *Ex = dyn_cast(Condition); if (!Ex) return UnknownVal(); uint64_t bits = 0; bool bitsInit = false; while (const CastExpr *CE = dyn_cast(Ex)) { QualType T = CE->getType(); if (!T->isIntegralOrEnumerationType()) return UnknownVal(); uint64_t newBits = Ctx.getTypeSize(T); if (!bitsInit || newBits < bits) { bitsInit = true; bits = newBits; } Ex = CE->getSubExpr(); } // We reached a non-cast. Is it a symbolic value? QualType T = Ex->getType(); if (!bitsInit || !T->isIntegralOrEnumerationType() || Ctx.getTypeSize(T) > bits) return UnknownVal(); return state->getSVal(Ex, LCtx); } #ifndef NDEBUG static const Stmt *getRightmostLeaf(const Stmt *Condition) { while (Condition) { const BinaryOperator *BO = dyn_cast(Condition); if (!BO || !BO->isLogicalOp()) { return Condition; } Condition = BO->getRHS()->IgnoreParens(); } return nullptr; } #endif // Returns the condition the branch at the end of 'B' depends on and whose value // has been evaluated within 'B'. // In most cases, the terminator condition of 'B' will be evaluated fully in // the last statement of 'B'; in those cases, the resolved condition is the // given 'Condition'. // If the condition of the branch is a logical binary operator tree, the CFG is // optimized: in that case, we know that the expression formed by all but the // rightmost leaf of the logical binary operator tree must be true, and thus // the branch condition is at this point equivalent to the truth value of that // rightmost leaf; the CFG block thus only evaluates this rightmost leaf // expression in its final statement. As the full condition in that case was // not evaluated, and is thus not in the SVal cache, we need to use that leaf // expression to evaluate the truth value of the condition in the current state // space. static const Stmt *ResolveCondition(const Stmt *Condition, const CFGBlock *B) { if (const Expr *Ex = dyn_cast(Condition)) Condition = Ex->IgnoreParens(); const BinaryOperator *BO = dyn_cast(Condition); if (!BO || !BO->isLogicalOp()) return Condition; assert(!B->getTerminator().isTemporaryDtorsBranch() && "Temporary destructor branches handled by processBindTemporary."); // For logical operations, we still have the case where some branches // use the traditional "merge" approach and others sink the branch // directly into the basic blocks representing the logical operation. // We need to distinguish between those two cases here. // The invariants are still shifting, but it is possible that the // last element in a CFGBlock is not a CFGStmt. Look for the last // CFGStmt as the value of the condition. CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); for (; I != E; ++I) { CFGElement Elem = *I; Optional CS = Elem.getAs(); if (!CS) continue; const Stmt *LastStmt = CS->getStmt(); assert(LastStmt == Condition || LastStmt == getRightmostLeaf(Condition)); return LastStmt; } llvm_unreachable("could not resolve condition"); } void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, NodeBuilderContext& BldCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { assert((!Condition || !isa(Condition)) && "CXXBindTemporaryExprs are handled by processBindTemporary."); const LocationContext *LCtx = Pred->getLocationContext(); PrettyStackTraceLocationContext StackCrashInfo(LCtx); currBldrCtx = &BldCtx; // Check for NULL conditions; e.g. "for(;;)" if (!Condition) { BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF); NullCondBldr.markInfeasible(false); NullCondBldr.generateNode(Pred->getState(), true, Pred); return; } if (const Expr *Ex = dyn_cast(Condition)) Condition = Ex->IgnoreParens(); Condition = ResolveCondition(Condition, BldCtx.getBlock()); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Condition->getLocStart(), "Error evaluating branch"); ExplodedNodeSet CheckersOutSet; getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet, Pred, *this); // We generated only sinks. if (CheckersOutSet.empty()) return; BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); for (NodeBuilder::iterator I = CheckersOutSet.begin(), E = CheckersOutSet.end(); E != I; ++I) { ExplodedNode *PredI = *I; if (PredI->isSink()) continue; ProgramStateRef PrevState = PredI->getState(); SVal X = PrevState->getSVal(Condition, PredI->getLocationContext()); if (X.isUnknownOrUndef()) { // Give it a chance to recover from unknown. if (const Expr *Ex = dyn_cast(Condition)) { if (Ex->getType()->isIntegralOrEnumerationType()) { // Try to recover some path-sensitivity. Right now casts of symbolic // integers that promote their values are currently not tracked well. // If 'Condition' is such an expression, try and recover the // underlying value and use that instead. SVal recovered = RecoverCastedSymbol(getStateManager(), PrevState, Condition, PredI->getLocationContext(), getContext()); if (!recovered.isUnknown()) { X = recovered; } } } } // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) { builder.generateNode(PrevState, true, PredI); builder.generateNode(PrevState, false, PredI); continue; } DefinedSVal V = X.castAs(); ProgramStateRef StTrue, StFalse; std::tie(StTrue, StFalse) = PrevState->assume(V); // Process the true branch. if (builder.isFeasible(true)) { if (StTrue) builder.generateNode(StTrue, true, PredI); else builder.markInfeasible(true); } // Process the false branch. if (builder.isFeasible(false)) { if (StFalse) builder.generateNode(StFalse, false, PredI); else builder.markInfeasible(false); } } currBldrCtx = nullptr; } /// The GDM component containing the set of global variables which have been /// previously initialized with explicit initializers. REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, llvm::ImmutableSet) void ExprEngine::processStaticInitializer(const DeclStmt *DS, NodeBuilderContext &BuilderCtx, ExplodedNode *Pred, clang::ento::ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); currBldrCtx = &BuilderCtx; const VarDecl *VD = cast(DS->getSingleDecl()); ProgramStateRef state = Pred->getState(); bool initHasRun = state->contains(VD); BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); if (!initHasRun) { state = state->add(VD); } builder.generateNode(state, initHasRun, Pred); builder.markInfeasible(!initHasRun); currBldrCtx = nullptr; } /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { ProgramStateRef state = builder.getState(); SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); // Three possibilities: // // (1) We know the computed label. // (2) The label is NULL (or some other constant), or Undefined. // (3) We have no clue about the label. Dispatch to all targets. // typedef IndirectGotoNodeBuilder::iterator iterator; if (Optional LV = V.getAs()) { const LabelDecl *L = LV->getLabel(); for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) { if (I.getLabel() == L) { builder.generateNode(I, state); return; } } llvm_unreachable("No block with label."); } if (V.getAs() || V.getAs()) { // Dispatch to the first target and mark it as a sink. //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); // FIXME: add checker visit. // UndefBranches.insert(N); return; } // This is really a catch-all. We don't support symbolics yet. // FIXME: Implement dispatch for symbolic pointers. for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) builder.generateNode(I, state); } void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExplodedNodeSet &Dst, const BlockEdge &L) { SaveAndRestore NodeContextRAII(currBldrCtx, &BC); getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this); } /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, const ReturnStmt *RS) { // See if we have any stale C++ allocator values. assert(areCXXNewAllocatorValuesClear(Pred->getState(), Pred->getLocationContext(), Pred->getStackFrame()->getParent())); // FIXME: We currently assert that temporaries are clear, as lifetime extended - // temporaries are not modelled correctly. When we materialize the temporary, - // we do createTemporaryRegionIfNeeded(), and the region changes, and also - // the respective destructor becomes automatic from temporary. - // So for now clean up the state manually before asserting. Ideally, the code - // above the assertion should go away, but the assertion should remain. + // temporaries are not always modelled correctly. In some cases when we + // materialize the temporary, we do createTemporaryRegionIfNeeded(), and + // the region changes, and also the respective destructor becomes automatic + // from temporary. So for now clean up the state manually before asserting. + // Ideally, the code above the assertion should go away, but the assertion + // should remain. { ExplodedNodeSet CleanUpTemporaries; NodeBuilder Bldr(Pred, CleanUpTemporaries, BC); ProgramStateRef State = Pred->getState(); const LocationContext *FromLC = Pred->getLocationContext(); const LocationContext *ToLC = FromLC->getCurrentStackFrame()->getParent(); const LocationContext *LC = FromLC; while (LC != ToLC) { assert(LC && "ToLC must be a parent of FromLC!"); for (auto I : State->get()) if (I.first.second == LC) State = State->remove(I.first); LC = LC->getParent(); } if (State != Pred->getState()) { Bldr.generateNode(Pred->getLocation(), State, Pred); assert(CleanUpTemporaries.size() <= 1); Pred = CleanUpTemporaries.empty() ? Pred : *CleanUpTemporaries.begin(); } } assert(areInitializedTemporariesClear(Pred->getState(), Pred->getLocationContext(), Pred->getStackFrame()->getParent())); + assert(areTemporaryMaterializationsClear(Pred->getState(), + Pred->getLocationContext(), + Pred->getStackFrame()->getParent())); PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); StateMgr.EndPath(Pred->getState()); ExplodedNodeSet Dst; if (Pred->getLocationContext()->inTopFrame()) { // Remove dead symbols. ExplodedNodeSet AfterRemovedDead; removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead); // Notify checkers. for (ExplodedNodeSet::iterator I = AfterRemovedDead.begin(), E = AfterRemovedDead.end(); I != E; ++I) { getCheckerManager().runCheckersForEndFunction(BC, Dst, *I, *this); } } else { getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); } Engine.enqueueEndOfFunction(Dst, RS); } /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { typedef SwitchNodeBuilder::iterator iterator; ProgramStateRef state = builder.getState(); const Expr *CondE = builder.getCondition(); SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); if (CondV_untested.isUndef()) { //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); // FIXME: add checker //UndefBranches.insert(N); return; } DefinedOrUnknownSVal CondV = CondV_untested.castAs(); ProgramStateRef DefaultSt = state; iterator I = builder.begin(), EI = builder.end(); bool defaultIsFeasible = I == EI; for ( ; I != EI; ++I) { // Successor may be pruned out during CFG construction. if (!I.getBlock()) continue; const CaseStmt *Case = I.getCase(); // Evaluate the LHS of the case value. llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext()); assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType())); // Get the RHS of the case, if it exists. llvm::APSInt V2; if (const Expr *E = Case->getRHS()) V2 = E->EvaluateKnownConstInt(getContext()); else V2 = V1; ProgramStateRef StateCase; if (Optional NL = CondV.getAs()) std::tie(StateCase, DefaultSt) = DefaultSt->assumeInclusiveRange(*NL, V1, V2); else // UnknownVal StateCase = DefaultSt; if (StateCase) builder.generateCaseStmtNode(I, StateCase); // Now "assume" that the case doesn't match. Add this state // to the default state (if it is feasible). if (DefaultSt) defaultIsFeasible = true; else { defaultIsFeasible = false; break; } } if (!defaultIsFeasible) return; // If we have switch(enum value), the default branch is not // feasible if all of the enum constants not covered by 'case:' statements // are not feasible values for the switch condition. // // Note that this isn't as accurate as it could be. Even if there isn't // a case for a particular enum value as long as that enum value isn't // feasible then it shouldn't be considered for making 'default:' reachable. const SwitchStmt *SS = builder.getSwitch(); const Expr *CondExpr = SS->getCond()->IgnoreParenImpCasts(); if (CondExpr->getType()->getAs()) { if (SS->isAllEnumCasesCovered()) return; } builder.generateDefaultCaseNode(DefaultSt); } //===----------------------------------------------------------------------===// // Transfer functions: Loads and stores. //===----------------------------------------------------------------------===// void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); if (const VarDecl *VD = dyn_cast(D)) { // C permits "extern void v", and if you cast the address to a valid type, // you can even do things with it. We simply pretend assert(Ex->isGLValue() || VD->getType()->isVoidType()); const LocationContext *LocCtxt = Pred->getLocationContext(); const Decl *D = LocCtxt->getDecl(); const auto *MD = D ? dyn_cast(D) : nullptr; const auto *DeclRefEx = dyn_cast(Ex); SVal V; bool IsReference; if (AMgr.options.shouldInlineLambdas() && DeclRefEx && DeclRefEx->refersToEnclosingVariableOrCapture() && MD && MD->getParent()->isLambda()) { // Lookup the field of the lambda. const CXXRecordDecl *CXXRec = MD->getParent(); llvm::DenseMap LambdaCaptureFields; FieldDecl *LambdaThisCaptureField; CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); const FieldDecl *FD = LambdaCaptureFields[VD]; if (!FD) { // When a constant is captured, sometimes no corresponding field is // created in the lambda object. assert(VD->getType().isConstQualified()); V = state->getLValue(VD, LocCtxt); IsReference = false; } else { Loc CXXThis = svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame()); SVal CXXThisVal = state->getSVal(CXXThis); V = state->getLValue(FD, CXXThisVal); IsReference = FD->getType()->isReferenceType(); } } else { V = state->getLValue(VD, LocCtxt); IsReference = VD->getType()->isReferenceType(); } // For references, the 'lvalue' is the pointer address stored in the // reference region. if (IsReference) { if (const MemRegion *R = V.getAsRegion()) V = state->getSVal(R); else V = UnknownVal(); } Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, ProgramPoint::PostLValueKind); return; } if (const EnumConstantDecl *ED = dyn_cast(D)) { assert(!Ex->isGLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; } if (const FunctionDecl *FD = dyn_cast(D)) { SVal V = svalBuilder.getFunctionPointer(FD); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, ProgramPoint::PostLValueKind); return; } if (isa(D) || isa(D)) { // FIXME: Compute lvalue of field pointers-to-member. // Right now we just use a non-null void pointer, so that it gives proper // results in boolean contexts. // FIXME: Maybe delegate this to the surrounding operator&. // Note how this expression is lvalue, however pointer-to-member is NonLoc. SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, currBldrCtx->blockCount()); state = state->assume(V.castAs(), true); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, ProgramPoint::PostLValueKind); return; } llvm_unreachable("Support for this Decl not implemented."); } /// VisitArraySubscriptExpr - Transfer function for array accesses void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, ExplodedNode *Pred, ExplodedNodeSet &Dst){ const Expr *Base = A->getBase()->IgnoreParens(); const Expr *Idx = A->getIdx()->IgnoreParens(); ExplodedNodeSet CheckerPreStmt; getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); ExplodedNodeSet EvalSet; StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); bool IsVectorType = A->getBase()->getType()->isVectorType(); // The "like" case is for situations where C standard prohibits the type to // be an lvalue, e.g. taking the address of a subscript of an expression of // type "void *". bool IsGLValueLike = A->isGLValue() || (A->getType().isCForbiddenLValueType() && !AMgr.getLangOpts().CPlusPlus); for (auto *Node : CheckerPreStmt) { const LocationContext *LCtx = Node->getLocationContext(); ProgramStateRef state = Node->getState(); if (IsGLValueLike) { QualType T = A->getType(); // One of the forbidden LValue types! We still need to have sensible // symbolic locations to represent this stuff. Note that arithmetic on // void pointers is a GCC extension. if (T->isVoidType()) T = getContext().CharTy; SVal V = state->getLValue(T, state->getSVal(Idx, LCtx), state->getSVal(Base, LCtx)); Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, ProgramPoint::PostLValueKind); } else if (IsVectorType) { // FIXME: non-glvalue vector reads are not modelled. Bldr.generateNode(A, Node, state, nullptr); } else { llvm_unreachable("Array subscript should be an lValue when not \ a vector and not a forbidden lvalue type"); } } getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); } /// VisitMemberExpr - Transfer function for member expressions. void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Prechecks eventually go in ::Visit(). ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); ExplodedNodeSet EvalSet; ValueDecl *Member = M->getMemberDecl(); // Handle static member variables and enum constants accessed via // member syntax. if (isa(Member) || isa(Member)) { for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { VisitCommonDeclRefExpr(M, Member, *I, EvalSet); } } else { StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); ExplodedNodeSet Tmp; for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { ProgramStateRef state = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); Expr *BaseExpr = M->getBase(); // Handle C++ method calls. if (const CXXMethodDecl *MD = dyn_cast(Member)) { if (MD->isInstance()) state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal MDVal = svalBuilder.getFunctionPointer(MD); state = state->BindExpr(M, LCtx, MDVal); Bldr.generateNode(M, *I, state); continue; } // Handle regular struct fields / member variables. state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal baseExprVal = state->getSVal(BaseExpr, LCtx); FieldDecl *field = cast(Member); SVal L = state->getLValue(field, baseExprVal); if (M->isGLValue() || M->getType()->isArrayType()) { // We special-case rvalues of array type because the analyzer cannot // reason about them, since we expect all regions to be wrapped in Locs. // We instead treat these as lvalues and assume that they will decay to // pointers as soon as they are used. if (!M->isGLValue()) { assert(M->getType()->isArrayType()); const ImplicitCastExpr *PE = dyn_cast((*I)->getParentMap().getParentIgnoreParens(M)); if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } } if (field->getType()->isReferenceType()) { if (const MemRegion *R = L.getAsRegion()) L = state->getSVal(R); else L = UnknownVal(); } Bldr.generateNode(M, *I, state->BindExpr(M, LCtx, L), nullptr, ProgramPoint::PostLValueKind); } else { Bldr.takeNodes(*I); evalLoad(Tmp, M, M, *I, state, L); Bldr.addNodes(Tmp); } } } getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, M, *this); } void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ExplodedNodeSet AfterPreSet; getCheckerManager().runCheckersForPreStmt(AfterPreSet, Pred, AE, *this); // For now, treat all the arguments to C11 atomics as escaping. // FIXME: Ideally we should model the behavior of the atomics precisely here. ExplodedNodeSet AfterInvalidateSet; StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx); for (ExplodedNodeSet::iterator I = AfterPreSet.begin(), E = AfterPreSet.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); SmallVector ValuesToInvalidate; for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++) { const Expr *SubExpr = AE->getSubExprs()[SI]; SVal SubExprVal = State->getSVal(SubExpr, LCtx); ValuesToInvalidate.push_back(SubExprVal); } State = State->invalidateRegions(ValuesToInvalidate, AE, currBldrCtx->blockCount(), LCtx, /*CausedByPointerEscape*/true, /*Symbols=*/nullptr); SVal ResultVal = UnknownVal(); State = State->BindExpr(AE, LCtx, ResultVal); Bldr.generateNode(AE, *I, State, nullptr, ProgramPoint::PostStmtKind); } getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); } // A value escapes in three possible cases: // (1) We are binding to something that is not a memory region. // (2) We are binding to a MemrRegion that does not have stack storage. // (3) We are binding to a MemRegion with stack storage that the store // does not understand. ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, SVal Val, const LocationContext *LCtx) { // Are we storing to something that causes the value to "escape"? bool escapes = true; // TODO: Move to StoreManager. if (Optional regionLoc = Loc.getAs()) { escapes = !regionLoc->getRegion()->hasStackStorage(); if (!escapes) { // To test (3), generate a new state with the binding added. If it is // the same state, then it escapes (since the store cannot represent // the binding). // Do this only if we know that the store is not supposed to generate the // same state. SVal StoredVal = State->getSVal(regionLoc->getRegion()); if (StoredVal != Val) escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx))); } } // If our store can represent the binding and we aren't storing to something // that doesn't have local storage then just return and have the simulation // state continue as is. if (!escapes) return State; // Otherwise, find all symbols referenced by 'val' that we are tracking // and stop tracking them. CollectReachableSymbolsCallback Scanner = State->scanReachableSymbols(Val); const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); State = getCheckerManager().runCheckersForPointerEscape(State, EscapedSymbols, /*CallEvent*/ nullptr, PSK_EscapeOnBind, nullptr); return State; } ProgramStateRef ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, const InvalidatedSymbols *Invalidated, ArrayRef ExplicitRegions, ArrayRef Regions, const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) { if (!Invalidated || Invalidated->empty()) return State; if (!Call) return getCheckerManager().runCheckersForPointerEscape(State, *Invalidated, nullptr, PSK_EscapeOther, &ITraits); // If the symbols were invalidated by a call, we want to find out which ones // were invalidated directly due to being arguments to the call. InvalidatedSymbols SymbolsDirectlyInvalidated; for (ArrayRef::iterator I = ExplicitRegions.begin(), E = ExplicitRegions.end(); I != E; ++I) { if (const SymbolicRegion *R = (*I)->StripCasts()->getAs()) SymbolsDirectlyInvalidated.insert(R->getSymbol()); } InvalidatedSymbols SymbolsIndirectlyInvalidated; for (InvalidatedSymbols::const_iterator I=Invalidated->begin(), E = Invalidated->end(); I!=E; ++I) { SymbolRef sym = *I; if (SymbolsDirectlyInvalidated.count(sym)) continue; SymbolsIndirectlyInvalidated.insert(sym); } if (!SymbolsDirectlyInvalidated.empty()) State = getCheckerManager().runCheckersForPointerEscape(State, SymbolsDirectlyInvalidated, Call, PSK_DirectEscapeOnCall, &ITraits); // Notify about the symbols that get indirectly invalidated by the call. if (!SymbolsIndirectlyInvalidated.empty()) State = getCheckerManager().runCheckersForPointerEscape(State, SymbolsIndirectlyInvalidated, Call, PSK_IndirectEscapeOnCall, &ITraits); return State; } /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore and (soon) VisitDeclStmt, and others. void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, SVal location, SVal Val, bool atDeclInit, const ProgramPoint *PP) { const LocationContext *LC = Pred->getLocationContext(); PostStmt PS(StoreE, LC); if (!PP) PP = &PS; // Do a previsit of the bind. ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, StoreE, *this, *PP); StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); // If the location is not a 'Loc', it will already be handled by // the checkers. There is nothing left to do. if (!location.getAs()) { const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr, /*tag*/nullptr); ProgramStateRef state = Pred->getState(); state = processPointerEscapedOnBind(state, location, Val, LC); Bldr.generateNode(L, state, Pred); return; } for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { ExplodedNode *PredI = *I; ProgramStateRef state = PredI->getState(); state = processPointerEscapedOnBind(state, location, Val, LC); // When binding the value, pass on the hint that this is a initialization. // For initializations, we do not need to inform clients of region // changes. state = state->bindLoc(location.castAs(), Val, LC, /* notifyChanges = */ !atDeclInit); const MemRegion *LocReg = nullptr; if (Optional LocRegVal = location.getAs()) { LocReg = LocRegVal->getRegion(); } const ProgramPoint L = PostStore(StoreE, LC, LocReg, nullptr); Bldr.generateNode(L, state, PredI); } } /// evalStore - Handle the semantics of a store via an assignment. /// @param Dst The node set to store generated state nodes /// @param AssignE The assignment expression if the store happens in an /// assignment. /// @param LocationE The location expression that is stored to. /// @param state The current simulation state /// @param location The location to store the value /// @param Val The value to be stored void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *LocationE, ExplodedNode *Pred, ProgramStateRef state, SVal location, SVal Val, const ProgramPointTag *tag) { // Proceed with the store. We use AssignE as the anchor for the PostStore // ProgramPoint if it is non-NULL, and LocationE otherwise. const Expr *StoreE = AssignE ? AssignE : LocationE; // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false); if (Tmp.empty()) return; if (location.isUndef()) return; for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) evalBind(Dst, StoreE, *NI, location, Val, false); } void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *NodeEx, const Expr *BoundEx, ExplodedNode *Pred, ProgramStateRef state, SVal location, const ProgramPointTag *tag, QualType LoadTy) { assert(!location.getAs() && "location cannot be a NonLoc."); // Are we loading from a region? This actually results in two loads; one // to fetch the address of the referenced value and one to fetch the // referenced value. if (const TypedValueRegion *TR = dyn_cast_or_null(location.getAsRegion())) { QualType ValTy = TR->getValueType(); if (const ReferenceType *RT = ValTy->getAs()) { static SimpleProgramPointTag loadReferenceTag(TagProviderName, "Load Reference"); ExplodedNodeSet Tmp; evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state, location, &loadReferenceTag, getContext().getPointerType(RT->getPointeeType())); // Perform the load from the referenced value. for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { state = (*I)->getState(); location = state->getSVal(BoundEx, (*I)->getLocationContext()); evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy); } return; } } evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy); } void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, const Expr *NodeEx, const Expr *BoundEx, ExplodedNode *Pred, ProgramStateRef state, SVal location, const ProgramPointTag *tag, QualType LoadTy) { assert(NodeEx); assert(BoundEx); // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, tag, true); if (Tmp.empty()) return; StmtNodeBuilder Bldr(Tmp, Dst, *currBldrCtx); if (location.isUndef()) return; // Proceed with the load. for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { state = (*NI)->getState(); const LocationContext *LCtx = (*NI)->getLocationContext(); SVal V = UnknownVal(); if (location.isValid()) { if (LoadTy.isNull()) LoadTy = BoundEx->getType(); V = state->getSVal(location.castAs(), LoadTy); } Bldr.generateNode(NodeEx, *NI, state->BindExpr(BoundEx, LCtx, V), tag, ProgramPoint::PostLoadKind); } } void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *NodeEx, const Stmt *BoundEx, ExplodedNode *Pred, ProgramStateRef state, SVal location, const ProgramPointTag *tag, bool isLoad) { StmtNodeBuilder BldrTop(Pred, Dst, *currBldrCtx); // Early checks for performance reason. if (location.isUnknown()) { return; } ExplodedNodeSet Src; BldrTop.takeNodes(Pred); StmtNodeBuilder Bldr(Pred, Src, *currBldrCtx); if (Pred->getState() != state) { // Associate this new state with an ExplodedNode. // FIXME: If I pass null tag, the graph is incorrect, e.g for // int *p; // p = 0; // *p = 0xDEADBEEF; // "p = 0" is not noted as "Null pointer value stored to 'p'" but // instead "int *p" is noted as // "Variable 'p' initialized to a null pointer value" static SimpleProgramPointTag tag(TagProviderName, "Location"); Bldr.generateNode(NodeEx, Pred, state, &tag); } ExplodedNodeSet Tmp; getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad, NodeEx, BoundEx, *this); BldrTop.addNodes(Tmp); } std::pair ExprEngine::geteagerlyAssumeBinOpBifurcationTags() { static SimpleProgramPointTag eagerlyAssumeBinOpBifurcationTrue(TagProviderName, "Eagerly Assume True"), eagerlyAssumeBinOpBifurcationFalse(TagProviderName, "Eagerly Assume False"); return std::make_pair(&eagerlyAssumeBinOpBifurcationTrue, &eagerlyAssumeBinOpBifurcationFalse); } void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, const Expr *Ex) { StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx); for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { ExplodedNode *Pred = *I; // Test if the previous node was as the same expression. This can happen // when the expression fails to evaluate to anything meaningful and // (as an optimization) we don't generate a node. ProgramPoint P = Pred->getLocation(); if (!P.getAs() || P.castAs().getStmt() != Ex) { continue; } ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(Ex, Pred->getLocationContext()); Optional SEV = V.getAs(); if (SEV && SEV->isExpression()) { const std::pair &tags = geteagerlyAssumeBinOpBifurcationTags(); ProgramStateRef StateTrue, StateFalse; std::tie(StateTrue, StateFalse) = state->assume(*SEV); // First assume that the condition is true. if (StateTrue) { SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateTrue, tags.first); } // Next, assume that the condition is false. if (StateFalse) { SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateFalse, tags.second); } } } } void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); // We have processed both the inputs and the outputs. All of the outputs // should evaluate to Locs. Nuke all of their values. // FIXME: Some day in the future it would be nice to allow a "plug-in" // which interprets the inline asm and stores proper results in the // outputs. ProgramStateRef state = Pred->getState(); for (const Expr *O : A->outputs()) { SVal X = state->getSVal(O, Pred->getLocationContext()); assert (!X.getAs()); // Should be an Lval, or unknown, undef. if (Optional LV = X.getAs()) state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); } Bldr.generateNode(A, Pred, state); } void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(A, Pred, Pred->getState()); } //===----------------------------------------------------------------------===// // Visualization. //===----------------------------------------------------------------------===// #ifndef NDEBUG static ExprEngine* GraphPrintCheckerState; static SourceManager* GraphPrintSourceManager; namespace llvm { template<> struct DOTGraphTraits : public DefaultDOTGraphTraits { DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} // FIXME: Since we do not cache error nodes in ExprEngine now, this does not // work. static std::string getNodeAttributes(const ExplodedNode *N, void*) { return ""; } // De-duplicate some source location pretty-printing. static void printLocation(raw_ostream &Out, SourceLocation SLoc) { if (SLoc.isFileID()) { Out << "\\lline=" << GraphPrintSourceManager->getExpansionLineNumber(SLoc) << " col=" << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) << "\\l"; } } static std::string getNodeLabel(const ExplodedNode *N, void*){ std::string sbuf; llvm::raw_string_ostream Out(sbuf); // Program Location. ProgramPoint Loc = N->getLocation(); switch (Loc.getKind()) { case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" << Loc.castAs().getBlock()->getBlockID(); break; } case ProgramPoint::BlockExitKind: assert (false); break; case ProgramPoint::CallEnterKind: Out << "CallEnter"; break; case ProgramPoint::CallExitBeginKind: Out << "CallExitBegin"; break; case ProgramPoint::CallExitEndKind: Out << "CallExitEnd"; break; case ProgramPoint::PostStmtPurgeDeadSymbolsKind: Out << "PostStmtPurgeDeadSymbols"; break; case ProgramPoint::PreStmtPurgeDeadSymbolsKind: Out << "PreStmtPurgeDeadSymbols"; break; case ProgramPoint::EpsilonKind: Out << "Epsilon Point"; break; case ProgramPoint::LoopExitKind: { LoopExit LE = Loc.castAs(); Out << "LoopExit: " << LE.getLoopStmt()->getStmtClassName(); break; } case ProgramPoint::PreImplicitCallKind: { ImplicitCallPoint PC = Loc.castAs(); Out << "PreCall: "; // FIXME: Get proper printing options. PC.getDecl()->print(Out, LangOptions()); printLocation(Out, PC.getLocation()); break; } case ProgramPoint::PostImplicitCallKind: { ImplicitCallPoint PC = Loc.castAs(); Out << "PostCall: "; // FIXME: Get proper printing options. PC.getDecl()->print(Out, LangOptions()); printLocation(Out, PC.getLocation()); break; } case ProgramPoint::PostInitializerKind: { Out << "PostInitializer: "; const CXXCtorInitializer *Init = Loc.castAs().getInitializer(); if (const FieldDecl *FD = Init->getAnyMember()) Out << *FD; else { QualType Ty = Init->getTypeSourceInfo()->getType(); Ty = Ty.getLocalUnqualifiedType(); LangOptions LO; // FIXME. Ty.print(Out, LO); } break; } case ProgramPoint::BlockEdgeKind: { const BlockEdge &E = Loc.castAs(); Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" << E.getDst()->getBlockID() << ')'; if (const Stmt *T = E.getSrc()->getTerminator()) { SourceLocation SLoc = T->getLocStart(); Out << "\\|Terminator: "; LangOptions LO; // FIXME. E.getSrc()->printTerminator(Out, LO); if (SLoc.isFileID()) { Out << "\\lline=" << GraphPrintSourceManager->getExpansionLineNumber(SLoc) << " col=" << GraphPrintSourceManager->getExpansionColumnNumber(SLoc); } if (isa(T)) { const Stmt *Label = E.getDst()->getLabel(); if (Label) { if (const CaseStmt *C = dyn_cast(Label)) { Out << "\\lcase "; LangOptions LO; // FIXME. if (C->getLHS()) C->getLHS()->printPretty(Out, nullptr, PrintingPolicy(LO)); if (const Stmt *RHS = C->getRHS()) { Out << " .. "; RHS->printPretty(Out, nullptr, PrintingPolicy(LO)); } Out << ":"; } else { assert (isa(Label)); Out << "\\ldefault:"; } } else Out << "\\l(implicit) default:"; } else if (isa(T)) { // FIXME } else { Out << "\\lCondition: "; if (*E.getSrc()->succ_begin() == E.getDst()) Out << "true"; else Out << "false"; } Out << "\\l"; } break; } default: { const Stmt *S = Loc.castAs().getStmt(); assert(S != nullptr && "Expecting non-null Stmt"); Out << S->getStmtClassName() << ' ' << (const void*) S << ' '; LangOptions LO; // FIXME. S->printPretty(Out, nullptr, PrintingPolicy(LO)); printLocation(Out, S->getLocStart()); if (Loc.getAs()) Out << "\\lPreStmt\\l;"; else if (Loc.getAs()) Out << "\\lPostLoad\\l;"; else if (Loc.getAs()) Out << "\\lPostStore\\l"; else if (Loc.getAs()) Out << "\\lPostLValue\\l"; else if (Loc.getAs()) Out << "\\lPostAllocatorCall\\l"; break; } } ProgramStateRef state = N->getState(); Out << "\\|StateID: " << (const void*) state.get() << " NodeID: " << (const void*) N << "\\|"; state->printDOT(Out, N->getLocationContext()); Out << "\\l"; if (const ProgramPointTag *tag = Loc.getTag()) { Out << "\\|Tag: " << tag->getTagDescription(); Out << "\\l"; } return Out.str(); } }; } // end llvm namespace #endif void ExprEngine::ViewGraph(bool trim) { #ifndef NDEBUG if (trim) { std::vector Src; // Flush any outstanding reports to make sure we cover all the nodes. // This does not cause them to get displayed. for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) const_cast(*I)->FlushReports(BR); // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { ExplodedNode *N = const_cast(EI->begin()->getErrorNode()); if (N) Src.push_back(N); } ViewGraph(Src); } else { GraphPrintCheckerState = this; GraphPrintSourceManager = &getContext().getSourceManager(); llvm::ViewGraph(*G.roots_begin(), "ExprEngine"); GraphPrintCheckerState = nullptr; GraphPrintSourceManager = nullptr; } #endif } void ExprEngine::ViewGraph(ArrayRef Nodes) { #ifndef NDEBUG GraphPrintCheckerState = this; GraphPrintSourceManager = &getContext().getSourceManager(); std::unique_ptr TrimmedG(G.trim(Nodes)); if (!TrimmedG.get()) llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; else llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedExprEngine"); GraphPrintCheckerState = nullptr; GraphPrintSourceManager = nullptr; #endif } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 9c95507ba572..2c1e858da25e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1,715 +1,726 @@ //===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the C++ expression evaluation engine. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens(); ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); state = createTemporaryRegionIfNeeded(state, LCtx, tempExpr, ME); Bldr.generateNode(ME, Pred, state); } // FIXME: This is the sort of code that should eventually live in a Core // checker rather than as a special case in ExprEngine. void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &Call) { SVal ThisVal; bool AlwaysReturnsLValue; if (const CXXConstructorCall *Ctor = dyn_cast(&Call)) { assert(Ctor->getDecl()->isTrivial()); assert(Ctor->getDecl()->isCopyOrMoveConstructor()); ThisVal = Ctor->getCXXThisVal(); AlwaysReturnsLValue = false; } else { assert(cast(Call.getDecl())->isTrivial()); assert(cast(Call.getDecl())->getOverloadedOperator() == OO_Equal); ThisVal = cast(Call).getCXXThisVal(); AlwaysReturnsLValue = true; } const LocationContext *LCtx = Pred->getLocationContext(); ExplodedNodeSet Dst; Bldr.takeNodes(Pred); SVal V = Call.getArgSVal(0); // If the value being copied is not unknown, load from its location to get // an aggregate rvalue. if (Optional L = V.getAs()) V = Pred->getState()->getSVal(*L); else assert(V.isUnknownOrUndef()); const Expr *CallExpr = Call.getOriginExpr(); evalBind(Dst, CallExpr, Pred, ThisVal, V, true); PostStmt PS(CallExpr, LCtx); for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); if (AlwaysReturnsLValue) State = State->BindExpr(CallExpr, LCtx, ThisVal); else State = bindReturnValue(Call, LCtx, State); Bldr.generateNode(PS, State, *I); } } SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue, QualType &Ty, bool &IsArray) { SValBuilder &SVB = State->getStateManager().getSValBuilder(); ASTContext &Ctx = SVB.getContext(); while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) { Ty = AT->getElementType(); LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue); IsArray = true; } return LValue; } const MemRegion * ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE, ExplodedNode *Pred, const ConstructionContext *CC, EvalCallOptions &CallOpts) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); // See if we're constructing an existing region by looking at the // current construction context. if (CC) { if (const Stmt *TriggerStmt = CC->getTriggerStmt()) { if (const CXXNewExpr *CNE = dyn_cast(TriggerStmt)) { if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { // TODO: Detect when the allocator returns a null pointer. // Constructor shall not be called in this case. if (const SubRegion *MR = dyn_cast_or_null( getCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) { if (CNE->isArray()) { // TODO: In fact, we need to call the constructor for every // allocated element, not just the first one! CallOpts.IsArrayCtorOrDtor = true; return getStoreManager().GetElementZeroRegion( MR, CNE->getType()->getPointeeType()); } return MR; } } } else if (auto *DS = dyn_cast(TriggerStmt)) { const auto *Var = cast(DS->getSingleDecl()); SVal LValue = State->getLValue(Var, LCtx); QualType Ty = Var->getType(); LValue = makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor); return LValue.getAsRegion(); } else if (isa(TriggerStmt)) { // TODO: We should construct into a CXXBindTemporaryExpr or a // MaterializeTemporaryExpr around the call-expression on the previous // stack frame. Currently we re-bind the temporary to the correct region // later, but that's not semantically correct. This of course does not // apply when we're in the top frame. But if we are in an inlined // function, we should be able to take the call-site CFG element, // and it should contain (but right now it wouldn't) some sort of // construction context that'd give us the right temporary expression. CallOpts.IsTemporaryCtorOrDtor = true; return MRMgr.getCXXTempObjectRegion(CE, LCtx); } else if (isa(TriggerStmt)) { CallOpts.IsTemporaryCtorOrDtor = true; return MRMgr.getCXXTempObjectRegion(CE, LCtx); } // TODO: Consider other directly initialized elements. } else if (const CXXCtorInitializer *Init = CC->getTriggerInit()) { assert(Init->isAnyMemberInitializer()); const CXXMethodDecl *CurCtor = cast(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); const ValueDecl *Field; SVal FieldVal; if (Init->isIndirectMemberInitializer()) { Field = Init->getIndirectMember(); FieldVal = State->getLValue(Init->getIndirectMember(), ThisVal); } else { Field = Init->getMember(); FieldVal = State->getLValue(Init->getMember(), ThisVal); } QualType Ty = Field->getType(); FieldVal = makeZeroElementRegion(State, FieldVal, Ty, CallOpts.IsArrayCtorOrDtor); return FieldVal.getAsRegion(); } // FIXME: This will eventually need to handle new-expressions as well. // Don't forget to update the pre-constructor initialization code in // ExprEngine::VisitCXXConstructExpr. } // If we couldn't find an existing region to construct into, assume we're // constructing a temporary. Notify the caller of our failure. CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; return MRMgr.getCXXTempObjectRegion(CE, LCtx); } const CXXConstructExpr * ExprEngine::findDirectConstructorForCurrentCFGElement() { // Go backward in the CFG to see if the previous element (ignoring // destructors) was a CXXConstructExpr. If so, that constructor // was constructed directly into an existing region. // This process is essentially the inverse of that performed in // findElementDirectlyInitializedByCurrentConstructor(). if (currStmtIdx == 0) return nullptr; const CFGBlock *B = getBuilderContext().getBlock(); unsigned int PreviousStmtIdx = currStmtIdx - 1; CFGElement Previous = (*B)[PreviousStmtIdx]; while (Previous.getAs() && PreviousStmtIdx > 0) { --PreviousStmtIdx; Previous = (*B)[PreviousStmtIdx]; } if (Optional PrevStmtElem = Previous.getAs()) { if (auto *CtorExpr = dyn_cast(PrevStmtElem->getStmt())) { return CtorExpr; } } return nullptr; } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); const MemRegion *Target = nullptr; // FIXME: Handle arrays, which run the same constructor for every element. // For now, we just run the first constructor (which should still invalidate // the entire array). EvalCallOptions CallOpts; auto C = getCurrentCFGElement().getAs(); const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; const CXXBindTemporaryExpr *BTE = nullptr; + const MaterializeTemporaryExpr *MTE = nullptr; switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts); if (CC && AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG() && !CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion && CallOpts.IsTemporaryCtorOrDtor) { - // May as well be a ReturnStmt. - BTE = dyn_cast(CC->getTriggerStmt()); + MTE = CC->getMaterializedTemporary(); + if (!MTE || MTE->getStorageDuration() == SD_FullExpression) { + // If the temporary is lifetime-extended, don't save the BTE, + // because we don't need a temporary destructor, but an automatic + // destructor. The cast may fail because it may as well be a ReturnStmt. + BTE = dyn_cast(CC->getTriggerStmt()); + } } break; } case CXXConstructExpr::CK_VirtualBase: // Make sure we are not calling virtual base class initializers twice. // Only the most-derived object should initialize virtual base classes. if (const Stmt *Outer = LCtx->getCurrentStackFrame()->getCallSite()) { const CXXConstructExpr *OuterCtor = dyn_cast(Outer); if (OuterCtor) { switch (OuterCtor->getConstructionKind()) { case CXXConstructExpr::CK_NonVirtualBase: case CXXConstructExpr::CK_VirtualBase: // Bail out! destNodes.Add(Pred); return; case CXXConstructExpr::CK_Complete: case CXXConstructExpr::CK_Delegating: break; } } } // FALLTHROUGH case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would // be initialized as aggregates without a constructor call, so we may have // a base class constructed directly into an initializer list without // having the derived-class constructor call on the previous stack frame. // Initializer lists may be nested into more initializer lists that // correspond to surrounding aggregate initializations. // FIXME: For now this code essentially bails out. We need to find the // correct target region and set it. // FIXME: Instead of relying on the ParentMap, we should have the // trigger-statement (InitListExpr in this case) passed down from CFG or // otherwise always available during construction. if (dyn_cast_or_null(LCtx->getParentMap().getParent(CE))) { MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); Target = MRMgr.getCXXTempObjectRegion(CE, LCtx); CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } // FALLTHROUGH case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { Target = ThisVal.getAsRegion(); } else { // Cast to the base type. bool IsVirtual = (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), IsVirtual); Target = BaseVal.getAsRegion(); } break; } } CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = CEMgr.getCXXConstructorCall(CE, Target, State, LCtx); ExplodedNodeSet DstPreVisit; getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); // FIXME: Is it possible and/or useful to do this before PreStmt? ExplodedNodeSet PreInitialized; { StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx); for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), E = DstPreVisit.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); if (CE->requiresZeroInitialization()) { // Type of the zero doesn't matter. SVal ZeroVal = svalBuilder.makeZeroVal(getContext().CharTy); // FIXME: Once we properly handle constructors in new-expressions, we'll // need to invalidate the region before setting a default value, to make // sure there aren't any lingering bindings around. This probably needs // to happen regardless of whether or not the object is zero-initialized // to handle random fields of a placement-initialized object picking up // old bindings. We might only want to do it when we need to, though. // FIXME: This isn't actually correct for arrays -- we need to zero- // initialize the entire array, not just the first element -- but our // handling of arrays everywhere else is weak as well, so this shouldn't // actually make things worse. Placement new makes this tricky as well, // since it's then possible to be initializing one part of a multi- // dimensional array. State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal, LCtx); } if (BTE) { State = addInitializedTemporary(State, BTE, LCtx, cast(Target)); } + if (MTE) { + State = addTemporaryMaterialization(State, MTE, LCtx, + cast(Target)); + } + Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, ProgramPoint::PreStmtKind); } } ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, *Call, *this); ExplodedNodeSet DstEvaluated; StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); if (CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) performTrivialCopy(Bldr, *I, *Call); } else { for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); } // If the CFG was contructed without elements for temporary destructors // and the just-called constructor created a temporary object then // stop exploration if the temporary object has a noreturn constructor. // This can lose coverage because the destructor, if it were present // in the CFG, would be called at the end of the full expression or // later (for life-time extended temporaries) -- but avoids infeasible // paths when no-return temporary destructors are used for assertions. const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); if (Target && isa(Target) && Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { // If we've inlined the constructor, then DstEvaluated would be empty. // In this case we still want a sink, which could be implemented // in processCallExit. But we don't have that implemented at the moment, // so if you hit this assertion, see if you can avoid inlining // the respective constructor when analyzer-config cfg-temporary-dtors // is set to false. // Otherwise there's nothing wrong with inlining such constructor. assert(!DstEvaluated.empty() && "We should not have inlined this constructor!"); for (ExplodedNode *N : DstEvaluated) { Bldr.generateSink(CE, N, N->getState()); } // There is no need to run the PostCall and PostStmt checker // callbacks because we just generated sinks on all nodes in th // frontier. return; } } ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, const EvalCallOptions &CallOpts) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Call->getSourceRange().getBegin(), "Error evaluating destructor"); ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); ExplodedNodeSet DstInvalidated; StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); } void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), CNE->getStartLoc(), "Error evaluating New Allocator Call"); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); ExplodedNodeSet DstPostCall; StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); for (auto I : DstPreCall) { // FIXME: Provide evalCall for checkers? defaultEvalCall(CallBldr, I, *Call); } // If the call is inlined, DstPostCall will be empty and we bail out now. // Store return value of operator new() for future use, until the actual // CXXNewExpr gets processed. ExplodedNodeSet DstPostValue; StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx); for (auto I : DstPostCall) { // FIXME: Because CNE serves as the "call site" for the allocator (due to // lack of a better expression in the AST), the conjured return value symbol // is going to be of the same type (C++ object pointer type). Technically // this is not correct because the operator new's prototype always says that // it returns a 'void *'. So we should change the type of the symbol, // and then evaluate the cast over the symbolic pointer from 'void *' to // the object pointer type. But without changing the symbol's type it // is breaking too much to evaluate the no-op symbolic cast over it, so we // skip it for now. ProgramStateRef State = I->getState(); SVal RetVal = State->getSVal(CNE, LCtx); // If this allocation function is not declared as non-throwing, failures // /must/ be signalled by exceptions, and thus the return value will never // be NULL. -fno-exceptions does not influence this semantics. // FIXME: GCC has a -fcheck-new option, which forces it to consider the case // where new can return NULL. If we end up supporting that option, we can // consider adding a check for it here. // C++11 [basic.stc.dynamic.allocation]p3. if (const FunctionDecl *FD = CNE->getOperatorNew()) { QualType Ty = FD->getType(); if (const auto *ProtoType = Ty->getAs()) if (!ProtoType->isNothrow(getContext())) State = State->assume(RetVal.castAs(), true); } ValueBldr.generateNode(CNE, I, setCXXNewAllocatorValue(State, CNE, LCtx, RetVal)); } ExplodedNodeSet DstPostPostCallCallback; getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, DstPostValue, *Call, *this); for (auto I : DstPostPostCallCallback) { getCheckerManager().runCheckersForNewAllocator( CNE, getCXXNewAllocatorValue(I->getState(), CNE, LCtx), Dst, I, *this); } } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Much of this should eventually migrate to CXXAllocatorCall. // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); SVal symVal = UnknownVal(); FunctionDecl *FD = CNE->getOperatorNew(); bool IsStandardGlobalOpNewFunction = FD->isReplaceableGlobalAllocationFunction(); ProgramStateRef State = Pred->getState(); // Retrieve the stored operator new() return value. if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { symVal = getCXXNewAllocatorValue(State, CNE, LCtx); State = clearCXXNewAllocatorValue(State, CNE, LCtx); } // We assume all standard global 'operator new' functions allocate memory in // heap. We realize this is an approximation that might not correctly model // a custom global allocator. if (symVal.isUnknown()) { if (IsStandardGlobalOpNewFunction) symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); else symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(), blockCount); } CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, // we should be using the usual pre-/(default-)eval-/post-call checks here. State = Call->invalidateRegions(blockCount); if (!State) return; // If this allocation function is not declared as non-throwing, failures // /must/ be signalled by exceptions, and thus the return value will never // be NULL. -fno-exceptions does not influence this semantics. // FIXME: GCC has a -fcheck-new option, which forces it to consider the case // where new can return NULL. If we end up supporting that option, we can // consider adding a check for it here. // C++11 [basic.stc.dynamic.allocation]p3. if (FD) { QualType Ty = FD->getType(); if (const auto *ProtoType = Ty->getAs()) if (!ProtoType->isNothrow(getContext())) if (auto dSymVal = symVal.getAs()) State = State->assume(*dSymVal, true); } } StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); SVal Result = symVal; if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. if (const SubRegion *NewReg = dyn_cast_or_null(symVal.getAsRegion())) { QualType ObjTy = CNE->getType()->getAs()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); Result = loc::MemRegionVal(EleReg); } State = State->BindExpr(CNE, Pred->getLocationContext(), Result); Bldr.generateNode(CNE, Pred, State); return; } // FIXME: Once we have proper support for CXXConstructExprs inside // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), CNE->getPlacementArg(0)->getType()); } // Bind the address of the object, then check to see if we cached out. State = State->BindExpr(CNE, LCtx, Result); ExplodedNode *NewN = Bldr.generateNode(CNE, Pred, State); if (!NewN) return; // If the type is not a record, we won't have a CXXConstructExpr as an // initializer. Copy the value over. if (const Expr *Init = CNE->getInitializer()) { if (!isa(Init)) { assert(Bldr.getResults().size() == 1); Bldr.takeNodes(NewN); evalBind(Dst, CNE, NewN, Result, State->getSVal(Init, LCtx), /*FirstInit=*/IsStandardGlobalOpNewFunction); } } } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef state = Pred->getState(); Bldr.generateNode(CDE, Pred, state); } void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const VarDecl *VD = CS->getExceptionDecl(); if (!VD) { Dst.Add(Pred); return; } const LocationContext *LCtx = Pred->getLocationContext(); SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(), currBldrCtx->blockCount()); ProgramStateRef state = Pred->getState(); state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx); StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(CS, Pred, state); } void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); // Get the this object region from StoreManager. const LocationContext *LCtx = Pred->getLocationContext(); const MemRegion *R = svalBuilder.getRegionManager().getCXXThisRegion( getContext().getCanonicalType(TE->getType()), LCtx); ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(loc::MemRegionVal(R)); Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); } void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LocCtxt = Pred->getLocationContext(); // Get the region of the lambda itself. const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( LE, LocCtxt); SVal V = loc::MemRegionVal(R); ProgramStateRef State = Pred->getState(); // If we created a new MemRegion for the lambda, we should explicitly bind // the captures. CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), e = LE->capture_init_end(); i != e; ++i, ++CurField) { FieldDecl *FieldForCapture = *CurField; SVal FieldLoc = State->getLValue(FieldForCapture, V); SVal InitVal; if (!FieldForCapture->hasCapturedVLAType()) { Expr *InitExpr = *i; assert(InitExpr && "Capture missing initialization expression"); InitVal = State->getSVal(InitExpr, LocCtxt); } else { // The field stores the length of a captured variable-length array. // These captures don't have initialization expressions; instead we // get the length from the VLAType size expression. Expr *SizeExpr = FieldForCapture->getCapturedVLAType()->getSizeExpr(); InitVal = State->getSVal(SizeExpr, LocCtxt); } State = State->bindLoc(FieldLoc, InitVal, LocCtxt); } // Decay the Loc into an RValue, because there might be a // MaterializeTemporaryExpr node above this one which expects the bound value // to be an RValue. SVal LambdaRVal = State->getSVal(R); ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); // FIXME: is this the right program point kind? Bldr.generateNode(LE, Pred, State->BindExpr(LE, LocCtxt, LambdaRVal), nullptr, ProgramPoint::PostLValueKind); // FIXME: Move all post/pre visits to ::Visit(). getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); } diff --git a/clang/test/Analysis/lifetime-extension.cpp b/clang/test/Analysis/lifetime-extension.cpp index 5e3c5dde0a8b..93605fc44d15 100644 --- a/clang/test/Analysis/lifetime-extension.cpp +++ b/clang/test/Analysis/lifetime-extension.cpp @@ -1,46 +1,195 @@ -// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=debug.ExprInspection -verify %s +// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -verify %s +// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true -DTEMPORARIES -verify %s void clang_analyzer_eval(bool); namespace pr17001_call_wrong_destructor { bool x; struct A { int *a; A() {} ~A() {} }; struct B : public A { B() {} ~B() { x = true; } }; void f() { { const A &a = B(); } clang_analyzer_eval(x); // expected-warning{{TRUE}} } } // end namespace pr17001_call_wrong_destructor namespace pr19539_crash_on_destroying_an_integer { struct A { int i; int j[2]; A() : i(1) { j[0] = 2; j[1] = 3; } ~A() {} }; void f() { const int &x = A().i; // no-crash const int &y = A().j[1]; // no-crash const int &z = (A().j[1], A().j[0]); // no-crash - // FIXME: All of these should be TRUE, but constructors aren't inlined. - clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == 1); + clang_analyzer_eval(y == 3); + clang_analyzer_eval(z == 2); +#ifdef TEMPORARIES + // expected-warning@-4{{TRUE}} + // expected-warning@-4{{TRUE}} + // expected-warning@-4{{TRUE}} +#else + // expected-warning@-8{{UNKNOWN}} + // expected-warning@-8{{UNKNOWN}} + // expected-warning@-8{{UNKNOWN}} +#endif } } // end namespace pr19539_crash_on_destroying_an_integer + +namespace maintain_original_object_address_on_lifetime_extension { +class C { + C **after, **before; + +public: + bool x; + + C(bool x, C **after, C **before) : x(x), after(after), before(before) { + *before = this; + } + + // Don't track copies in our tests. + C(const C &c) : x(c.x), after(nullptr), before(nullptr) {} + + ~C() { if (after) *after = this; } + + operator bool() const { return x; } +}; + +void f1() { + C *after, *before; + { + const C &c = C(true, &after, &before); + } + clang_analyzer_eval(after == before); +#ifdef TEMPORARIES + // expected-warning@-2{{TRUE}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif +} + +void f2() { + C *after, *before; + C c = C(1, &after, &before); + clang_analyzer_eval(after == before); +#ifdef TEMPORARIES + // expected-warning@-2{{TRUE}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif +} + +void f3(bool coin) { + C *after, *before; + { + const C &c = coin ? C(true, &after, &before) : C(false, &after, &before); + } + clang_analyzer_eval(after == before); +#ifdef TEMPORARIES + // expected-warning@-2{{TRUE}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif +} + +void f4(bool coin) { + C *after, *before; + { + // no-crash + const C &c = C(coin, &after, &before) ?: C(false, &after, &before); + } + // FIXME: Add support for lifetime extension through binary conditional + // operator. Ideally also add support for the binary conditional operator in + // C++. Because for now it calls the constructor for the condition twice. + if (coin) { + clang_analyzer_eval(after == before); +#ifdef TEMPORARIES + // expected-warning@-2{{The left operand of '==' is a garbage value}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif + } else { + clang_analyzer_eval(after == before); +#ifdef TEMPORARIES + // Seems to work at the moment, but also seems accidental. + // Feel free to break. + // expected-warning@-4{{TRUE}} +#else + // expected-warning@-6{{UNKNOWN}} +#endif + } +} + +void f5() { + C *after, *before; + { + const bool &x = C(true, &after, &before).x; // no-crash + } + // FIXME: Should be TRUE. Should not warn about garbage value. + clang_analyzer_eval(after == before); +#ifdef TEMPORARIES + // expected-warning@-2{{The left operand of '==' is a garbage value}} +#else + // expected-warning@-4{{UNKNOWN}} +#endif +} +} // end namespace maintain_original_object_address_on_lifetime_extension + +namespace maintain_original_object_address_on_move { +class C { + int *x; + +public: + C() : x(nullptr) {} + C(int *x) : x(x) {} + C(const C &c) = delete; + C(C &&c) : x(c.x) { c.x = nullptr; } + C &operator=(C &&c) { + x = c.x; + c.x = nullptr; + return *this; + } + ~C() { + // This was triggering the division by zero warning in f1() and f2(): + // Because move-elision materialization was incorrectly causing the object + // to be relocated from one address to another before move, but destructor + // was operating on the old address, it was still thinking that 'x' is set. + if (x) + *x = 0; + } +}; + +void f1() { + int x = 1; + // &x is replaced with nullptr in move-constructor before the temporary dies. + C c = C(&x); + // Hence x was not set to 0 yet. + 1 / x; // no-warning +} +void f2() { + int x = 1; + C c; + // &x is replaced with nullptr in move-assignment before the temporary dies. + c = C(&x); + // Hence x was not set to 0 yet. + 1 / x; // no-warning +} +} // end namespace maintain_original_object_address_on_move diff --git a/clang/test/Analysis/temporaries.cpp b/clang/test/Analysis/temporaries.cpp index 2587e4ed56a8..48a1c5751d85 100644 --- a/clang/test/Analysis/temporaries.cpp +++ b/clang/test/Analysis/temporaries.cpp @@ -1,910 +1,937 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11 extern bool clang_analyzer_eval(bool); extern bool clang_analyzer_warnIfReached(); void clang_analyzer_checkInlined(bool); #include "Inputs/system-header-simulator-cxx.h"; struct Trivial { Trivial(int x) : value(x) {} int value; }; struct NonTrivial : public Trivial { NonTrivial(int x) : Trivial(x) {} ~NonTrivial(); }; Trivial getTrivial() { return Trivial(42); // no-warning } const Trivial &getTrivialRef() { return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'Trivial' returned to caller}} } NonTrivial getNonTrivial() { return NonTrivial(42); // no-warning } const NonTrivial &getNonTrivialRef() { return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}} } namespace rdar13265460 { struct TrivialSubclass : public Trivial { TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {} int anotherValue; }; TrivialSubclass getTrivialSub() { TrivialSubclass obj(1); obj.value = 42; obj.anotherValue = -42; return obj; } void testImmediate() { TrivialSubclass obj = getTrivialSub(); clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}} clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}} clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}} clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}} } void testMaterializeTemporaryExpr() { const TrivialSubclass &ref = getTrivialSub(); clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} const Trivial &baseRef = getTrivialSub(); clang_analyzer_eval(baseRef.value == 42); // expected-warning{{TRUE}} } } namespace rdar13281951 { struct Derived : public Trivial { Derived(int value) : Trivial(value), value2(-value) {} int value2; }; void test() { Derived obj(1); obj.value = 42; const Trivial * const &pointerRef = &obj; clang_analyzer_eval(pointerRef->value == 42); // expected-warning{{TRUE}} } } namespace compound_literals { struct POD { int x, y; }; struct HasCtor { HasCtor(int x, int y) : x(x), y(y) {} int x, y; }; struct HasDtor { int x, y; ~HasDtor(); }; struct HasCtorDtor { HasCtorDtor(int x, int y) : x(x), y(y) {} ~HasCtorDtor(); int x, y; }; void test() { clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}} clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}} #if __cplusplus >= 201103L clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}} // FIXME: should be TRUE, but we don't inline the constructors of // temporaries because we can't model their destructors yet. clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}} #endif } } namespace destructors { struct Dtor { ~Dtor(); }; extern bool coin(); extern bool check(const Dtor &); void testPR16664andPR18159Crash() { // Regression test: we used to assert here when tmp dtors are enabled. // PR16664 and PR18159 if (coin() && (coin() || coin() || check(Dtor()))) { Dtor(); } } #ifdef TEMPORARY_DTORS struct NoReturnDtor { ~NoReturnDtor() __attribute__((noreturn)); }; void noReturnTemp(int *x) { if (! x) NoReturnDtor(); *x = 47; // no warning } void noReturnInline(int **x) { NoReturnDtor(); } void callNoReturn() { int *x; noReturnInline(&x); *x = 47; // no warning } extern bool check(const NoReturnDtor &); void testConsistencyIf(int i) { if (i != 5) return; if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) { clang_analyzer_eval(true); // no warning, unreachable code } } void testConsistencyTernary(int i) { (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i != 5) return; (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; clang_analyzer_eval(true); // no warning, unreachable code } // Regression test: we used to assert here. // PR16664 and PR18159 void testConsistencyNested(int i) { extern bool compute(bool); if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i != 5) return; if (compute(i == 5 && (i == 4 || compute(true) || compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || i != 4) { clang_analyzer_eval(true); // expected-warning{{TRUE}} } if (compute(i == 5 && (i == 4 || i == 4 || compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || i != 4) { clang_analyzer_eval(true); // no warning, unreachable code } } // PR16664 and PR18159 void testConsistencyNestedSimple(bool value) { if (value) { if (!value || check(NoReturnDtor())) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedComplex(bool value) { if (value) { if (!value || !value || check(NoReturnDtor())) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedWarning(bool value) { if (value) { if (!value || value || check(NoReturnDtor())) { clang_analyzer_eval(true); // expected-warning{{TRUE}} } } } // PR16664 and PR18159 void testConsistencyNestedComplexMidBranch(bool value) { if (value) { if (!value || !value || check(NoReturnDtor()) || value) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedComplexNestedBranch(bool value) { if (value) { if (!value || (!value || check(NoReturnDtor()) || value)) { clang_analyzer_eval(true); // no warning, unreachable code } } } // PR16664 and PR18159 void testConsistencyNestedVariableModification(bool value) { bool other = true; if (value) { if (!other || !value || (other = false) || check(NoReturnDtor()) || !other) { clang_analyzer_eval(true); // no warning, unreachable code } } } void testTernaryNoReturnTrueBranch(bool value) { if (value) { bool b = value && (value ? check(NoReturnDtor()) : true); clang_analyzer_eval(true); // no warning, unreachable code } } void testTernaryNoReturnFalseBranch(bool value) { if (value) { bool b = !value && !value ? true : check(NoReturnDtor()); clang_analyzer_eval(true); // no warning, unreachable code } } void testTernaryIgnoreNoreturnBranch(bool value) { if (value) { bool b = !value && !value ? check(NoReturnDtor()) : true; clang_analyzer_eval(true); // expected-warning{{TRUE}} } } void testTernaryTrueBranchReached(bool value) { value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}} check(NoReturnDtor()); } void testTernaryFalseBranchReached(bool value) { value ? check(NoReturnDtor()) : clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void testLoop() { for (int i = 0; i < 10; ++i) { if (i < 3 && (i >= 2 || check(NoReturnDtor()))) { clang_analyzer_eval(true); // no warning, unreachable code } } } bool testRecursiveFrames(bool isInner) { if (isInner || (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}} check(NoReturnDtor()) || testRecursiveFrames(true)) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } } void testRecursiveFramesStart() { testRecursiveFrames(false); } void testLambdas() { []() { check(NoReturnDtor()); } != nullptr || check(Dtor()); } void testGnuExpressionStatements(int v) { ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23; clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23; clang_analyzer_warnIfReached(); // no warning, unreachable code } void testGnuExpressionStatementsDestructionPoint(int v) { // In normal context, the temporary destructor runs at the end of the full // statement, thus the last statement is reached. (++v, check(NoReturnDtor()), v == 42), clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} // GNU expression statements execute temporary destructors within the // blocks, thus the last statement is not reached. ({ ++v; check(NoReturnDtor()); v == 42; }), clang_analyzer_warnIfReached(); // no warning, unreachable code } void testMultipleTemporaries(bool value) { if (value) { // FIXME: Find a way to verify construction order. // ~Dtor should run before ~NoReturnDtor() because construction order is // guaranteed by comma operator. if (!value || check((NoReturnDtor(), Dtor())) || value) { clang_analyzer_eval(true); // no warning, unreachable code } } } void testBinaryOperatorShortcut(bool value) { if (value) { if (false && false && check(NoReturnDtor()) && true) { clang_analyzer_eval(true); } } } void testIfAtEndOfLoop() { int y = 0; while (true) { if (y > 0) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } ++y; // Test that the CFG gets hooked up correctly when temporary destructors // are handled after a statically known branch condition. if (true) (void)0; else (void)check(NoReturnDtor()); } } void testTernaryAtEndOfLoop() { int y = 0; while (true) { if (y > 0) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } ++y; // Test that the CFG gets hooked up correctly when temporary destructors // are handled after a statically known branch condition. true ? (void)0 : (void)check(NoReturnDtor()); } } void testNoReturnInComplexCondition() { check(Dtor()) && (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor()); clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void testSequencingOfConditionalTempDtors(bool b) { b || (check(Dtor()), check(NoReturnDtor())); clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void testSequencingOfConditionalTempDtors2(bool b) { (b || check(Dtor())), check(NoReturnDtor()); clang_analyzer_warnIfReached(); // no warning, unreachable code } void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) { b || (check(Dtor()) + check(NoReturnDtor())); clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} } void f(Dtor d = Dtor()); void testDefaultParameters() { f(); } struct DefaultParam { DefaultParam(int, const Dtor& d = Dtor()); ~DefaultParam(); }; void testDefaultParamConstructorsInLoops() { while (true) { // FIXME: This exact pattern triggers the temporary cleanup logic // to fail when adding a 'clean' state. DefaultParam(42); DefaultParam(42); } } void testDefaultParamConstructorsInTernariesInLoops(bool value) { while (true) { // FIXME: This exact pattern triggers the temporary cleanup logic // to visit the bind-temporary logic with a state that already has that // temporary marked as executed. value ? DefaultParam(42) : DefaultParam(42); } } #else // !TEMPORARY_DTORS // Test for fallback logic that conservatively stops exploration after // executing a temporary constructor for a class with a no-return destructor // when temporary destructors are not enabled in the CFG. struct CtorWithNoReturnDtor { CtorWithNoReturnDtor() = default; CtorWithNoReturnDtor(int x) { clang_analyzer_checkInlined(false); // no-warning } ~CtorWithNoReturnDtor() __attribute__((noreturn)); }; void testDefaultContructorWithNoReturnDtor() { CtorWithNoReturnDtor(); clang_analyzer_warnIfReached(); // no-warning } void testLifeExtensionWithNoReturnDtor() { const CtorWithNoReturnDtor &c = CtorWithNoReturnDtor(); // This represents an (expected) loss of coverage, since the destructor // of the lifetime-exended temporary is executed at the end of // scope. clang_analyzer_warnIfReached(); // no-warning } #if __cplusplus >= 201103L CtorWithNoReturnDtor returnNoReturnDtor() { return {1}; // no-crash } #endif #endif // TEMPORARY_DTORS } void testStaticMaterializeTemporaryExpr() { static const Trivial &ref = getTrivial(); clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}} static const Trivial &directRef = Trivial(42); clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}} #if __has_feature(cxx_thread_local) thread_local static const Trivial &threadRef = getTrivial(); clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}} thread_local static const Trivial &threadDirectRef = Trivial(42); clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}} #endif } namespace PR16629 { struct A { explicit A(int* p_) : p(p_) {} int* p; }; extern void escape(const A*[]); extern void check(int); void callEscape(const A& a) { const A* args[] = { &a }; escape(args); } void testNoWarning() { int x; callEscape(A(&x)); check(x); // Analyzer used to give a "x is uninitialized warning" here } void set(const A*a[]) { *a[0]->p = 47; } void callSet(const A& a) { const A* args[] = { &a }; set(args); } void testConsistency() { int x; callSet(A(&x)); clang_analyzer_eval(x == 47); // expected-warning{{TRUE}} } } namespace PR32088 { void testReturnFromStmtExprInitializer() { // We shouldn't try to destroy the object pointed to by `obj' upon return. const NonTrivial &obj = ({ return; // no-crash NonTrivial(42); }); } } namespace CopyToTemporaryCorrectly { class Super { public: void m() { mImpl(); } virtual void mImpl() = 0; }; class Sub : public Super { public: Sub(const int &p) : j(p) {} virtual void mImpl() override { // Used to be undefined pointer dereference because we didn't copy // the subclass data (j) to the temporary object properly. (void)(j + 1); // no-warning if (j != 22) { clang_analyzer_warnIfReached(); // no-warning } } const int &j; }; void run() { int i = 22; Sub(i).m(); } } namespace test_return_temporary { class C { int x, y; public: C(int x, int y) : x(x), y(y) {} int getX() const { return x; } int getY() const { return y; } ~C() {} }; class D: public C { public: D() : C(1, 2) {} D(const D &d): C(d.getX(), d.getY()) {} }; C returnTemporaryWithVariable() { C c(1, 2); return c; } C returnTemporaryWithAnotherFunctionWithVariable() { return returnTemporaryWithVariable(); } C returnTemporaryWithCopyConstructionWithVariable() { return C(returnTemporaryWithVariable()); } C returnTemporaryWithConstruction() { return C(1, 2); } C returnTemporaryWithAnotherFunctionWithConstruction() { return returnTemporaryWithConstruction(); } C returnTemporaryWithCopyConstructionWithConstruction() { return C(returnTemporaryWithConstruction()); } D returnTemporaryWithVariableAndNonTrivialCopy() { D d; return d; } D returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy() { return returnTemporaryWithVariableAndNonTrivialCopy(); } D returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy() { return D(returnTemporaryWithVariableAndNonTrivialCopy()); } #if __cplusplus >= 201103L C returnTemporaryWithBraces() { return {1, 2}; } C returnTemporaryWithAnotherFunctionWithBraces() { return returnTemporaryWithBraces(); } C returnTemporaryWithCopyConstructionWithBraces() { return C(returnTemporaryWithBraces()); } #endif // C++11 void test() { C c1 = returnTemporaryWithVariable(); clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c1.getY() == 2); // expected-warning{{TRUE}} C c2 = returnTemporaryWithAnotherFunctionWithVariable(); clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c2.getY() == 2); // expected-warning{{TRUE}} C c3 = returnTemporaryWithCopyConstructionWithVariable(); clang_analyzer_eval(c3.getX() == 1); // expected-warning{{TRUE}} clang_analyzer_eval(c3.getY() == 2); // expected-warning{{TRUE}} C c4 = returnTemporaryWithConstruction(); clang_analyzer_eval(c4.getX() == 1); clang_analyzer_eval(c4.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif C c5 = returnTemporaryWithAnotherFunctionWithConstruction(); clang_analyzer_eval(c5.getX() == 1); clang_analyzer_eval(c5.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif C c6 = returnTemporaryWithCopyConstructionWithConstruction(); clang_analyzer_eval(c5.getX() == 1); clang_analyzer_eval(c5.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif #if __cplusplus >= 201103L C c7 = returnTemporaryWithBraces(); clang_analyzer_eval(c7.getX() == 1); clang_analyzer_eval(c7.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif C c8 = returnTemporaryWithAnotherFunctionWithBraces(); clang_analyzer_eval(c8.getX() == 1); clang_analyzer_eval(c8.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif C c9 = returnTemporaryWithCopyConstructionWithBraces(); clang_analyzer_eval(c9.getX() == 1); clang_analyzer_eval(c9.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif #endif // C++11 D d1 = returnTemporaryWithVariableAndNonTrivialCopy(); clang_analyzer_eval(d1.getX() == 1); clang_analyzer_eval(d1.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif D d2 = returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy(); clang_analyzer_eval(d2.getX() == 1); clang_analyzer_eval(d2.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif D d3 = returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy(); clang_analyzer_eval(d3.getX() == 1); clang_analyzer_eval(d3.getY() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } } // namespace test_return_temporary namespace test_temporary_object_expr_without_dtor { class C { int x; public: C(int x) : x(x) {} int getX() const { return x; } }; void test() { clang_analyzer_eval(C(3).getX() == 3); // expected-warning{{TRUE}} }; } namespace test_temporary_object_expr_with_dtor { class C { int x; public: C(int x) : x(x) {} ~C() {} int getX() const { return x; } }; void test(int coin) { clang_analyzer_eval(C(3).getX() == 3); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif const C &c1 = coin ? C(1) : C(2); if (coin) { clang_analyzer_eval(c1.getX() == 1); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } else { clang_analyzer_eval(c1.getX() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } C c2 = coin ? C(1) : C(2); if (coin) { clang_analyzer_eval(c2.getX() == 1); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } else { clang_analyzer_eval(c2.getX() == 2); #ifdef TEMPORARY_DTORS // expected-warning@-2{{TRUE}} #else // expected-warning@-4{{UNKNOWN}} #endif } } } // namespace test_temporary_object_expr namespace test_match_constructors_and_destructors { class C { public: int &x, &y; C(int &_x, int &_y) : x(_x), y(_y) { ++x; } C(const C &c): x(c.x), y(c.y) { ++x; } ~C() { ++y; } }; void test_simple_temporary() { int x = 0, y = 0; { const C &c = C(x, y); } // One constructor and one destructor. clang_analyzer_eval(x == 1); clang_analyzer_eval(y == 1); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } void test_simple_temporary_with_copy() { int x = 0, y = 0; { C c = C(x, y); } // Two constructors (temporary object expr and copy) and two destructors. clang_analyzer_eval(x == 2); clang_analyzer_eval(y == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } void test_ternary_temporary(int coin) { int x = 0, y = 0, z = 0, w = 0; { const C &c = coin ? C(x, y) : C(z, w); } // This time each branch contains an additional elidable copy constructor. if (coin) { clang_analyzer_eval(x == 2); clang_analyzer_eval(y == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} clang_analyzer_eval(w == 0); // expected-warning{{TRUE}} } else { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} clang_analyzer_eval(z == 2); clang_analyzer_eval(w == 2); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } } void test_ternary_temporary_with_copy(int coin) { int x = 0, y = 0, z = 0, w = 0; { C c = coin ? C(x, y) : C(z, w); } // Temporary expression, elidable copy within branch, // constructor for variable - 3 total. if (coin) { clang_analyzer_eval(x == 3); clang_analyzer_eval(y == 3); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif clang_analyzer_eval(z == 0); // expected-warning{{TRUE}} clang_analyzer_eval(w == 0); // expected-warning{{TRUE}} } else { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} clang_analyzer_eval(z == 3); clang_analyzer_eval(w == 3); #ifdef TEMPORARY_DTORS // expected-warning@-3{{TRUE}} // expected-warning@-3{{TRUE}} #else // expected-warning@-6{{UNKNOWN}} // expected-warning@-6{{UNKNOWN}} #endif } } } // namespace test_match_constructors_and_destructors +namespace dont_forget_destructor_around_logical_op { +int glob; + +class C { +public: + ~C() { glob = 1; } +}; + +C get(); + +bool is(C); + + +void test(int coin) { + // Here temporaries are being cleaned up after && is evaluated. There are two + // temporaries: the return value of get() and the elidable copy constructor + // of that return value into is(). According to the CFG, we need to cleanup + // both of them depending on whether the temporary corresponding to the + // return value of get() was initialized. However, for now we don't track + // temporaries returned from functions, so we take the wrong branch. + coin && is(get()); // no-crash + // FIXME: We should have called the destructor, i.e. should be TRUE, + // at least when we inline temporary destructors. + clang_analyzer_eval(glob == 1); // expected-warning{{UNKNOWN}} +} +} // namespace dont_forget_destructor_around_logical_op + #if __cplusplus >= 201103L namespace temporary_list_crash { class C { public: C() {} ~C() {} }; void test() { std::initializer_list{C(), C()}; // no-crash } } // namespace temporary_list_crash #endif // C++11