diff options
Diffstat (limited to 'clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h')
| -rw-r--r-- | clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h new file mode 100644 index 00000000..a01678ef --- /dev/null +++ b/clang-r353983/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -0,0 +1,574 @@ +//===- CoreEngine.h - Path-Sensitive Dataflow Engine ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H + +#include "clang/AST/Stmt.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <memory> +#include <utility> +#include <vector> + +namespace clang { + +class AnalyzerOptions; +class CXXBindTemporaryExpr; +class Expr; +class LabelDecl; + +namespace ento { + +class FunctionSummariesTy; +class SubEngine; + +//===----------------------------------------------------------------------===// +/// CoreEngine - Implements the core logic of the graph-reachability +/// analysis. It traverses the CFG and generates the ExplodedGraph. +/// Program "states" are treated as opaque void pointers. +/// The template class CoreEngine (which subclasses CoreEngine) +/// provides the matching component to the engine that knows the actual types +/// for states. Note that this engine only dispatches to transfer functions +/// at the statement and block-level. The analyses themselves must implement +/// any transfer function logic and the sub-expression level (if any). +class CoreEngine { + friend class CommonNodeBuilder; + friend class EndOfFunctionNodeBuilder; + friend class ExprEngine; + friend class IndirectGotoNodeBuilder; + friend class NodeBuilder; + friend struct NodeBuilderContext; + friend class SwitchNodeBuilder; + +public: + using BlocksExhausted = + std::vector<std::pair<BlockEdge, const ExplodedNode *>>; + + using BlocksAborted = + std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>; + +private: + SubEngine &SubEng; + + /// G - The simulation graph. Each node is a (location,state) pair. + mutable ExplodedGraph G; + + /// WList - A set of queued nodes that need to be processed by the + /// worklist algorithm. It is up to the implementation of WList to decide + /// the order that nodes are processed. + std::unique_ptr<WorkList> WList; + + /// BCounterFactory - A factory object for created BlockCounter objects. + /// These are used to record for key nodes in the ExplodedGraph the + /// number of times different CFGBlocks have been visited along a path. + BlockCounter::Factory BCounterFactory; + + /// The locations where we stopped doing work because we visited a location + /// too many times. + BlocksExhausted blocksExhausted; + + /// The locations where we stopped because the engine aborted analysis, + /// usually because it could not reason about something. + BlocksAborted blocksAborted; + + /// The information about functions shared by the whole translation unit. + /// (This data is owned by AnalysisConsumer.) + FunctionSummariesTy *FunctionSummaries; + + void generateNode(const ProgramPoint &Loc, + ProgramStateRef State, + ExplodedNode *Pred); + + void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred); + void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred); + void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred); + + void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred); + + void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred); + + void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, + ExplodedNode *Pred); + void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + const CFGBlock *B, ExplodedNode *Pred); + + /// Handle conditional logic for running static initializers. + void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred); + +private: + ExplodedNode *generateCallExitBeginNode(ExplodedNode *N, + const ReturnStmt *RS); + +public: + /// Construct a CoreEngine object to analyze the provided CFG. + CoreEngine(SubEngine &subengine, + FunctionSummariesTy *FS, + AnalyzerOptions &Opts); + + CoreEngine(const CoreEngine &) = delete; + CoreEngine &operator=(const CoreEngine &) = delete; + + /// getGraph - Returns the exploded graph. + ExplodedGraph &getGraph() { return G; } + + /// ExecuteWorkList - Run the worklist algorithm for a maximum number of + /// steps. Returns true if there is still simulation state on the worklist. + bool ExecuteWorkList(const LocationContext *L, unsigned Steps, + ProgramStateRef InitState); + + /// Returns true if there is still simulation state on the worklist. + bool ExecuteWorkListWithInitialState(const LocationContext *L, + unsigned Steps, + ProgramStateRef InitState, + ExplodedNodeSet &Dst); + + /// Dispatch the work list item based on the given location information. + /// Use Pred parameter as the predecessor state. + void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU); + + // Functions for external checking of whether we have unfinished work + bool wasBlockAborted() const { return !blocksAborted.empty(); } + bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } + bool hasWorkRemaining() const { return wasBlocksExhausted() || + WList->hasWork() || + wasBlockAborted(); } + + /// Inform the CoreEngine that a basic block was aborted because + /// it could not be completely analyzed. + void addAbortedBlock(const ExplodedNode *node, const CFGBlock *block) { + blocksAborted.push_back(std::make_pair(block, node)); + } + + WorkList *getWorkList() const { return WList.get(); } + + BlocksExhausted::const_iterator blocks_exhausted_begin() const { + return blocksExhausted.begin(); + } + + BlocksExhausted::const_iterator blocks_exhausted_end() const { + return blocksExhausted.end(); + } + + BlocksAborted::const_iterator blocks_aborted_begin() const { + return blocksAborted.begin(); + } + + BlocksAborted::const_iterator blocks_aborted_end() const { + return blocksAborted.end(); + } + + /// Enqueue the given set of nodes onto the work list. + void enqueue(ExplodedNodeSet &Set); + + /// Enqueue nodes that were created as a result of processing + /// a statement onto the work list. + void enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx); + + /// enqueue the nodes corresponding to the end of function onto the + /// end of path / work list. + void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS); + + /// Enqueue a single node created as a result of statement processing. + void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx); +}; + +// TODO: Turn into a calss. +struct NodeBuilderContext { + const CoreEngine &Eng; + const CFGBlock *Block; + const LocationContext *LC; + + NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N) + : Eng(E), Block(B), LC(N->getLocationContext()) { assert(B); } + + /// Return the CFGBlock associated with this builder. + const CFGBlock *getBlock() const { return Block; } + + /// Returns the number of times the current basic block has been + /// visited on the exploded graph path. + unsigned blockCount() const { + return Eng.WList->getBlockCounter().getNumVisited( + LC->getStackFrame(), + Block->getBlockID()); + } +}; + +/// \class NodeBuilder +/// This is the simplest builder which generates nodes in the +/// ExplodedGraph. +/// +/// The main benefit of the builder is that it automatically tracks the +/// frontier nodes (or destination set). This is the set of nodes which should +/// be propagated to the next step / builder. They are the nodes which have been +/// added to the builder (either as the input node set or as the newly +/// constructed nodes) but did not have any outgoing transitions added. +class NodeBuilder { + virtual void anchor(); + +protected: + const NodeBuilderContext &C; + + /// Specifies if the builder results have been finalized. For example, if it + /// is set to false, autotransitions are yet to be generated. + bool Finalized; + + bool HasGeneratedNodes = false; + + /// The frontier set - a set of nodes which need to be propagated after + /// the builder dies. + ExplodedNodeSet &Frontier; + + /// Checks if the results are ready. + virtual bool checkResults() { + return Finalized; + } + + bool hasNoSinksInFrontier() { + for (const auto I : Frontier) + if (I->isSink()) + return false; + return true; + } + + /// Allow subclasses to finalize results before result_begin() is executed. + virtual void finalizeResults() {} + + ExplodedNode *generateNodeImpl(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred, + bool MarkAsSink = false); + +public: + NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, bool F = true) + : C(Ctx), Finalized(F), Frontier(DstSet) { + Frontier.Add(SrcNode); + } + + NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, bool F = true) + : C(Ctx), Finalized(F), Frontier(DstSet) { + Frontier.insert(SrcSet); + assert(hasNoSinksInFrontier()); + } + + virtual ~NodeBuilder() = default; + + /// Generates a node in the ExplodedGraph. + ExplodedNode *generateNode(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred) { + return generateNodeImpl(PP, State, Pred, false); + } + + /// Generates a sink in the ExplodedGraph. + /// + /// When a node is marked as sink, the exploration from the node is stopped - + /// the node becomes the last node on the path and certain kinds of bugs are + /// suppressed. + ExplodedNode *generateSink(const ProgramPoint &PP, + ProgramStateRef State, + ExplodedNode *Pred) { + return generateNodeImpl(PP, State, Pred, true); + } + + const ExplodedNodeSet &getResults() { + finalizeResults(); + assert(checkResults()); + return Frontier; + } + + using iterator = ExplodedNodeSet::iterator; + + /// Iterators through the results frontier. + iterator begin() { + finalizeResults(); + assert(checkResults()); + return Frontier.begin(); + } + + iterator end() { + finalizeResults(); + return Frontier.end(); + } + + const NodeBuilderContext &getContext() { return C; } + bool hasGeneratedNodes() { return HasGeneratedNodes; } + + void takeNodes(const ExplodedNodeSet &S) { + for (const auto I : S) + Frontier.erase(I); + } + + void takeNodes(ExplodedNode *N) { Frontier.erase(N); } + void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); } + void addNodes(ExplodedNode *N) { Frontier.Add(N); } +}; + +/// \class NodeBuilderWithSinks +/// This node builder keeps track of the generated sink nodes. +class NodeBuilderWithSinks: public NodeBuilder { + void anchor() override; + +protected: + SmallVector<ExplodedNode*, 2> sinksGenerated; + ProgramPoint &Location; + +public: + NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, ProgramPoint &L) + : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} + + ExplodedNode *generateNode(ProgramStateRef State, + ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + return NodeBuilder::generateNode(LocalLoc, State, Pred); + } + + ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); + ExplodedNode *N = NodeBuilder::generateSink(LocalLoc, State, Pred); + if (N && N->isSink()) + sinksGenerated.push_back(N); + return N; + } + + const SmallVectorImpl<ExplodedNode*> &getSinks() const { + return sinksGenerated; + } +}; + +/// \class StmtNodeBuilder +/// This builder class is useful for generating nodes that resulted from +/// visiting a statement. The main difference from its parent NodeBuilder is +/// that it creates a statement specific ProgramPoint. +class StmtNodeBuilder: public NodeBuilder { + NodeBuilder *EnclosingBldr; + +public: + /// Constructs a StmtNodeBuilder. If the builder is going to process + /// nodes currently owned by another builder(with larger scope), use + /// Enclosing builder to transfer ownership. + StmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, + NodeBuilder *Enclosing = nullptr) + : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { + if (EnclosingBldr) + EnclosingBldr->takeNodes(SrcNode); + } + + StmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, + const NodeBuilderContext &Ctx, + NodeBuilder *Enclosing = nullptr) + : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { + if (EnclosingBldr) + for (const auto I : SrcSet) + EnclosingBldr->takeNodes(I); + } + + ~StmtNodeBuilder() override; + + using NodeBuilder::generateNode; + using NodeBuilder::generateSink; + + ExplodedNode *generateNode(const Stmt *S, + ExplodedNode *Pred, + ProgramStateRef St, + const ProgramPointTag *tag = nullptr, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); + return NodeBuilder::generateNode(L, St, Pred); + } + + ExplodedNode *generateSink(const Stmt *S, + ExplodedNode *Pred, + ProgramStateRef St, + const ProgramPointTag *tag = nullptr, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); + return NodeBuilder::generateSink(L, St, Pred); + } +}; + +/// BranchNodeBuilder is responsible for constructing the nodes +/// corresponding to the two branches of the if statement - true and false. +class BranchNodeBuilder: public NodeBuilder { + const CFGBlock *DstT; + const CFGBlock *DstF; + + bool InFeasibleTrue; + bool InFeasibleFalse; + + void anchor() override; + +public: + BranchNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, + const NodeBuilderContext &C, + const CFGBlock *dstT, const CFGBlock *dstF) + : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { + // The branch node builder does not generate autotransitions. + // If there are no successors it means that both branches are infeasible. + takeNodes(SrcNode); + } + + BranchNodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, + const NodeBuilderContext &C, + const CFGBlock *dstT, const CFGBlock *dstF) + : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { + takeNodes(SrcSet); + } + + ExplodedNode *generateNode(ProgramStateRef State, bool branch, + ExplodedNode *Pred); + + const CFGBlock *getTargetBlock(bool branch) const { + return branch ? DstT : DstF; + } + + void markInfeasible(bool branch) { + if (branch) + InFeasibleTrue = true; + else + InFeasibleFalse = true; + } + + bool isFeasible(bool branch) { + return branch ? !InFeasibleTrue : !InFeasibleFalse; + } +}; + +class IndirectGotoNodeBuilder { + CoreEngine& Eng; + const CFGBlock *Src; + const CFGBlock &DispatchBlock; + const Expr *E; + ExplodedNode *Pred; + +public: + IndirectGotoNodeBuilder(ExplodedNode *pred, const CFGBlock *src, + const Expr *e, const CFGBlock *dispatch, CoreEngine* eng) + : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} + + class iterator { + friend class IndirectGotoNodeBuilder; + + CFGBlock::const_succ_iterator I; + + iterator(CFGBlock::const_succ_iterator i) : I(i) {} + + public: + iterator &operator++() { ++I; return *this; } + bool operator!=(const iterator &X) const { return I != X.I; } + + const LabelDecl *getLabel() const { + return cast<LabelStmt>((*I)->getLabel())->getDecl(); + } + + const CFGBlock *getBlock() const { + return *I; + } + }; + + iterator begin() { return iterator(DispatchBlock.succ_begin()); } + iterator end() { return iterator(DispatchBlock.succ_end()); } + + ExplodedNode *generateNode(const iterator &I, + ProgramStateRef State, + bool isSink = false); + + const Expr *getTarget() const { return E; } + + ProgramStateRef getState() const { return Pred->State; } + + const LocationContext *getLocationContext() const { + return Pred->getLocationContext(); + } +}; + +class SwitchNodeBuilder { + CoreEngine& Eng; + const CFGBlock *Src; + const Expr *Condition; + ExplodedNode *Pred; + +public: + SwitchNodeBuilder(ExplodedNode *pred, const CFGBlock *src, + const Expr *condition, CoreEngine* eng) + : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} + + class iterator { + friend class SwitchNodeBuilder; + + CFGBlock::const_succ_reverse_iterator I; + + iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {} + + public: + iterator &operator++() { ++I; return *this; } + bool operator!=(const iterator &X) const { return I != X.I; } + bool operator==(const iterator &X) const { return I == X.I; } + + const CaseStmt *getCase() const { + return cast<CaseStmt>((*I)->getLabel()); + } + + const CFGBlock *getBlock() const { + return *I; + } + }; + + iterator begin() { return iterator(Src->succ_rbegin()+1); } + iterator end() { return iterator(Src->succ_rend()); } + + const SwitchStmt *getSwitch() const { + return cast<SwitchStmt>(Src->getTerminator()); + } + + ExplodedNode *generateCaseStmtNode(const iterator &I, + ProgramStateRef State); + + ExplodedNode *generateDefaultCaseNode(ProgramStateRef State, + bool isSink = false); + + const Expr *getCondition() const { return Condition; } + + ProgramStateRef getState() const { return Pred->State; } + + const LocationContext *getLocationContext() const { + return Pred->getLocationContext(); + } +}; + +} // namespace ento + +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H |
