1 //===- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -----*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the template classes ExplodedNode and ExplodedGraph, 10 // which represent a path-sensitive, intra-procedural "exploded graph". 11 // See "Precise interprocedural dataflow analysis via graph reachability" 12 // by Reps, Horwitz, and Sagiv 13 // (http://portal.acm.org/citation.cfm?id=199462) for the definition of an 14 // exploded graph. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H 19 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H 20 21 #include "clang/Analysis/AnalysisDeclContext.h" 22 #include "clang/Analysis/ProgramPoint.h" 23 #include "clang/Analysis/Support/BumpVector.h" 24 #include "clang/Basic/LLVM.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 28 #include "llvm/ADT/ArrayRef.h" 29 #include "llvm/ADT/DenseMap.h" 30 #include "llvm/ADT/DepthFirstIterator.h" 31 #include "llvm/ADT/FoldingSet.h" 32 #include "llvm/ADT/GraphTraits.h" 33 #include "llvm/ADT/STLExtras.h" 34 #include "llvm/ADT/SetVector.h" 35 #include "llvm/ADT/iterator_range.h" 36 #include "llvm/Support/Allocator.h" 37 #include "llvm/Support/Compiler.h" 38 #include <cassert> 39 #include <cstdint> 40 #include <memory> 41 #include <optional> 42 #include <utility> 43 #include <vector> 44 45 namespace clang { 46 47 class CFG; 48 class Decl; 49 class Expr; 50 class ParentMap; 51 class Stmt; 52 53 namespace ento { 54 55 class ExplodedGraph; 56 57 //===----------------------------------------------------------------------===// 58 // ExplodedGraph "implementation" classes. These classes are not typed to 59 // contain a specific kind of state. Typed-specialized versions are defined 60 // on top of these classes. 61 //===----------------------------------------------------------------------===// 62 63 // ExplodedNode is not constified all over the engine because we need to add 64 // successors to it at any time after creating it. 65 66 class ExplodedNode : public llvm::FoldingSetNode { 67 friend class BranchNodeBuilder; 68 friend class CoreEngine; 69 friend class EndOfFunctionNodeBuilder; 70 friend class ExplodedGraph; 71 friend class IndirectGotoNodeBuilder; 72 friend class NodeBuilder; 73 friend class SwitchNodeBuilder; 74 75 /// Efficiently stores a list of ExplodedNodes, or an optional flag. 76 /// 77 /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing 78 /// for the case when there is only one node in the group. This is a fairly 79 /// common case in an ExplodedGraph, where most nodes have only one 80 /// predecessor and many have only one successor. It can also be used to 81 /// store a flag rather than a node list, which ExplodedNode uses to mark 82 /// whether a node is a sink. If the flag is set, the group is implicitly 83 /// empty and no nodes may be added. 84 class NodeGroup { 85 // Conceptually a discriminated union. If the low bit is set, the node is 86 // a sink. If the low bit is not set, the pointer refers to the storage 87 // for the nodes in the group. 88 // This is not a PointerIntPair in order to keep the storage type opaque. 89 uintptr_t P; 90 91 public: P(Flag)92 NodeGroup(bool Flag = false) : P(Flag) { 93 assert(getFlag() == Flag); 94 } 95 96 ExplodedNode * const *begin() const; 97 98 ExplodedNode * const *end() const; 99 100 unsigned size() const; 101 empty()102 bool empty() const { return P == 0 || getFlag() != 0; } 103 104 /// Adds a node to the list. 105 /// 106 /// The group must not have been created with its flag set. 107 void addNode(ExplodedNode *N, ExplodedGraph &G); 108 109 /// Replaces the single node in this group with a new node. 110 /// 111 /// Note that this should only be used when you know the group was not 112 /// created with its flag set, and that the group is empty or contains 113 /// only a single node. 114 void replaceNode(ExplodedNode *node); 115 116 /// Returns whether this group was created with its flag set. getFlag()117 bool getFlag() const { 118 return (P & 1); 119 } 120 }; 121 122 /// Location - The program location (within a function body) associated 123 /// with this node. 124 const ProgramPoint Location; 125 126 /// State - The state associated with this node. 127 ProgramStateRef State; 128 129 /// Preds - The predecessors of this node. 130 NodeGroup Preds; 131 132 /// Succs - The successors of this node. 133 NodeGroup Succs; 134 135 int64_t Id; 136 137 public: ExplodedNode(const ProgramPoint & loc,ProgramStateRef state,int64_t Id,bool IsSink)138 explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, 139 int64_t Id, bool IsSink) 140 : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) { 141 assert(isSink() == IsSink); 142 } 143 144 /// getLocation - Returns the edge associated with the given node. getLocation()145 ProgramPoint getLocation() const { return Location; } 146 getLocationContext()147 const LocationContext *getLocationContext() const { 148 return getLocation().getLocationContext(); 149 } 150 getStackFrame()151 const StackFrameContext *getStackFrame() const { 152 return getLocation().getStackFrame(); 153 } 154 getCodeDecl()155 const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); } 156 getCFG()157 CFG &getCFG() const { return *getLocationContext()->getCFG(); } 158 159 const CFGBlock *getCFGBlock() const; 160 getParentMap()161 const ParentMap &getParentMap() const { 162 return getLocationContext()->getParentMap(); 163 } 164 getAnalysis()165 template <typename T> T &getAnalysis() const { 166 return *getLocationContext()->getAnalysis<T>(); 167 } 168 getState()169 const ProgramStateRef &getState() const { return State; } 170 getLocationAs()171 template <typename T> std::optional<T> getLocationAs() const & { 172 return Location.getAs<T>(); 173 } 174 175 /// Get the value of an arbitrary expression at this node. getSVal(const Stmt * S)176 SVal getSVal(const Stmt *S) const { 177 return getState()->getSVal(S, getLocationContext()); 178 } 179 Profile(llvm::FoldingSetNodeID & ID,const ProgramPoint & Loc,const ProgramStateRef & state,bool IsSink)180 static void Profile(llvm::FoldingSetNodeID &ID, 181 const ProgramPoint &Loc, 182 const ProgramStateRef &state, 183 bool IsSink) { 184 ID.Add(Loc); 185 ID.AddPointer(state.get()); 186 ID.AddBoolean(IsSink); 187 } 188 Profile(llvm::FoldingSetNodeID & ID)189 void Profile(llvm::FoldingSetNodeID& ID) const { 190 // We avoid copy constructors by not using accessors. 191 Profile(ID, Location, State, isSink()); 192 } 193 194 /// addPredeccessor - Adds a predecessor to the current node, and 195 /// in tandem add this node as a successor of the other node. 196 void addPredecessor(ExplodedNode *V, ExplodedGraph &G); 197 succ_size()198 unsigned succ_size() const { return Succs.size(); } pred_size()199 unsigned pred_size() const { return Preds.size(); } succ_empty()200 bool succ_empty() const { return Succs.empty(); } pred_empty()201 bool pred_empty() const { return Preds.empty(); } 202 isSink()203 bool isSink() const { return Succs.getFlag(); } 204 hasSinglePred()205 bool hasSinglePred() const { 206 return (pred_size() == 1); 207 } 208 getFirstPred()209 ExplodedNode *getFirstPred() { 210 return pred_empty() ? nullptr : *(pred_begin()); 211 } 212 getFirstPred()213 const ExplodedNode *getFirstPred() const { 214 return const_cast<ExplodedNode*>(this)->getFirstPred(); 215 } 216 getFirstSucc()217 ExplodedNode *getFirstSucc() { 218 return succ_empty() ? nullptr : *(succ_begin()); 219 } 220 getFirstSucc()221 const ExplodedNode *getFirstSucc() const { 222 return const_cast<ExplodedNode*>(this)->getFirstSucc(); 223 } 224 225 // Iterators over successor and predecessor vertices. 226 using succ_iterator = ExplodedNode * const *; 227 using succ_range = llvm::iterator_range<succ_iterator>; 228 229 using const_succ_iterator = const ExplodedNode * const *; 230 using const_succ_range = llvm::iterator_range<const_succ_iterator>; 231 232 using pred_iterator = ExplodedNode * const *; 233 using pred_range = llvm::iterator_range<pred_iterator>; 234 235 using const_pred_iterator = const ExplodedNode * const *; 236 using const_pred_range = llvm::iterator_range<const_pred_iterator>; 237 pred_begin()238 pred_iterator pred_begin() { return Preds.begin(); } pred_end()239 pred_iterator pred_end() { return Preds.end(); } preds()240 pred_range preds() { return {Preds.begin(), Preds.end()}; } 241 pred_begin()242 const_pred_iterator pred_begin() const { 243 return const_cast<ExplodedNode*>(this)->pred_begin(); 244 } pred_end()245 const_pred_iterator pred_end() const { 246 return const_cast<ExplodedNode*>(this)->pred_end(); 247 } preds()248 const_pred_range preds() const { return {Preds.begin(), Preds.end()}; } 249 succ_begin()250 succ_iterator succ_begin() { return Succs.begin(); } succ_end()251 succ_iterator succ_end() { return Succs.end(); } succs()252 succ_range succs() { return {Succs.begin(), Succs.end()}; } 253 succ_begin()254 const_succ_iterator succ_begin() const { 255 return const_cast<ExplodedNode*>(this)->succ_begin(); 256 } succ_end()257 const_succ_iterator succ_end() const { 258 return const_cast<ExplodedNode*>(this)->succ_end(); 259 } succs()260 const_succ_range succs() const { return {Succs.begin(), Succs.end()}; } 261 getID()262 int64_t getID() const { return Id; } 263 264 /// The node is trivial if it has only one successor, only one predecessor, 265 /// it's predecessor has only one successor, 266 /// and its program state is the same as the program state of the previous 267 /// node. 268 /// Trivial nodes may be skipped while printing exploded graph. 269 bool isTrivial() const; 270 271 /// If the node's program point corresponds to a statement, retrieve that 272 /// statement. Useful for figuring out where to put a warning or a note. 273 /// If the statement belongs to a body-farmed definition, 274 /// retrieve the call site for that definition. 275 const Stmt *getStmtForDiagnostics() const; 276 277 /// Find the next statement that was executed on this node's execution path. 278 /// Useful for explaining control flow that follows the current node. 279 /// If the statement belongs to a body-farmed definition, retrieve the 280 /// call site for that definition. 281 const Stmt *getNextStmtForDiagnostics() const; 282 283 /// Find the statement that was executed immediately before this node. 284 /// Useful when the node corresponds to a CFG block entrance. 285 /// If the statement belongs to a body-farmed definition, retrieve the 286 /// call site for that definition. 287 const Stmt *getPreviousStmtForDiagnostics() const; 288 289 /// Find the statement that was executed at or immediately before this node. 290 /// Useful when any nearby statement will do. 291 /// If the statement belongs to a body-farmed definition, retrieve the 292 /// call site for that definition. 293 const Stmt *getCurrentOrPreviousStmtForDiagnostics() const; 294 295 private: replaceSuccessor(ExplodedNode * node)296 void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); } replacePredecessor(ExplodedNode * node)297 void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } 298 }; 299 300 using InterExplodedGraphMap = 301 llvm::DenseMap<const ExplodedNode *, const ExplodedNode *>; 302 303 class ExplodedGraph { 304 protected: 305 friend class CoreEngine; 306 307 // Type definitions. 308 using NodeVector = std::vector<ExplodedNode *>; 309 310 /// The root of the simulation graph. Can be nullptr if the graph is empty or 311 /// if it was populated by `createUncachedNode()`. 312 ExplodedNode *Root = nullptr; 313 314 /// The nodes in the simulation graph which have been 315 /// specially marked as the endpoint of an abstract simulation path. 316 NodeVector EndNodes; 317 318 /// Nodes - The nodes in the graph. 319 llvm::FoldingSet<ExplodedNode> Nodes; 320 321 /// BVC - Allocator and context for allocating nodes and their predecessor 322 /// and successor groups. 323 BumpVectorContext BVC; 324 325 /// NumNodes - The number of nodes in the graph. 326 int64_t NumNodes = 0; 327 328 /// A list of recently allocated nodes that can potentially be recycled. 329 NodeVector ChangedNodes; 330 331 /// A list of nodes that can be reused. 332 NodeVector FreeNodes; 333 334 /// Determines how often nodes are reclaimed. 335 /// 336 /// If this is 0, nodes will never be reclaimed. 337 unsigned ReclaimNodeInterval = 0; 338 339 /// Counter to determine when to reclaim nodes. 340 unsigned ReclaimCounter; 341 342 public: 343 ExplodedGraph(); 344 ~ExplodedGraph(); 345 346 /// Get the root node of the graph. This may return nullptr if the graph is 347 /// empty or under construction. getRoot()348 ExplodedNode *getRoot() const { return Root; } 349 350 /// Retrieve the node associated with a (Location, State) pair, where the 351 /// 'Location' is a ProgramPoint in the CFG. If no node for this pair exists, 352 /// it is created. IsNew is set to true if the node was freshly created. 353 ExplodedNode *getNode(const ProgramPoint &L, ProgramStateRef State, 354 bool IsSink = false, 355 bool* IsNew = nullptr); 356 357 /// Create a node for a (Location, State) pair, but don't store it for 358 /// deduplication later. This is useful when copying some nodes from an 359 /// already completed ExplodedGraph for further processing. 360 ExplodedNode *createUncachedNode(const ProgramPoint &L, 361 ProgramStateRef State, 362 int64_t Id, 363 bool IsSink = false); 364 365 /// Mark a node as the root of the graph. Calling this is an error if the 366 /// graph already has a root node. designateAsRoot(ExplodedNode * V)367 void designateAsRoot(ExplodedNode *V) { 368 assert(V && "Cannot designate nullptr as root!"); 369 assert(!Root && "The graph already has a root, cannot designate another!"); 370 Root = V; 371 } 372 373 /// addEndOfPath - Add an untyped node to the set of EOP nodes. addEndOfPath(ExplodedNode * V)374 ExplodedNode *addEndOfPath(ExplodedNode *V) { 375 EndNodes.push_back(V); 376 return V; 377 } 378 num_eops()379 unsigned num_eops() const { return EndNodes.size(); } 380 empty()381 bool empty() const { return NumNodes == 0; } size()382 unsigned size() const { return NumNodes; } 383 reserve(unsigned NodeCount)384 void reserve(unsigned NodeCount) { Nodes.reserve(NodeCount); } 385 386 // Iterators. 387 using NodeTy = ExplodedNode; 388 using AllNodesTy = llvm::FoldingSet<ExplodedNode>; 389 using eop_iterator = NodeVector::iterator; 390 using const_eop_iterator = NodeVector::const_iterator; 391 using node_iterator = AllNodesTy::iterator; 392 using const_node_iterator = AllNodesTy::const_iterator; 393 nodes()394 llvm::iterator_range<node_iterator> nodes() { return Nodes; } 395 nodes()396 llvm::iterator_range<const_node_iterator> nodes() const { return Nodes; } 397 eop_begin()398 eop_iterator eop_begin() { return EndNodes.begin(); } 399 eop_end()400 eop_iterator eop_end() { return EndNodes.end(); } 401 eop_begin()402 const_eop_iterator eop_begin() const { return EndNodes.begin(); } 403 eop_end()404 const_eop_iterator eop_end() const { return EndNodes.end(); } 405 getAllocator()406 llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); } getNodeAllocator()407 BumpVectorContext &getNodeAllocator() { return BVC; } 408 409 using NodeMap = llvm::DenseMap<const ExplodedNode *, ExplodedNode *>; 410 411 /// Creates a trimmed version of the graph that only contains paths leading 412 /// to the given nodes. 413 /// 414 /// \param Nodes The nodes which must appear in the final graph. Presumably 415 /// these are end-of-path nodes (i.e. they have no successors). 416 /// \param[out] ForwardMap An optional map from nodes in this graph to nodes 417 /// in the returned graph. 418 /// \param[out] InverseMap An optional map from nodes in the returned graph to 419 /// nodes in this graph. 420 /// \returns The trimmed graph 421 std::unique_ptr<ExplodedGraph> 422 trim(ArrayRef<const NodeTy *> Nodes, 423 InterExplodedGraphMap *ForwardMap = nullptr, 424 InterExplodedGraphMap *InverseMap = nullptr) const; 425 426 /// Enable tracking of recently allocated nodes for potential reclamation 427 /// when calling reclaimRecentlyAllocatedNodes(). enableNodeReclamation(unsigned Interval)428 void enableNodeReclamation(unsigned Interval) { 429 ReclaimCounter = ReclaimNodeInterval = Interval; 430 } 431 432 /// Reclaim "uninteresting" nodes created since the last time this method 433 /// was called. 434 void reclaimRecentlyAllocatedNodes(); 435 436 /// Returns true if nodes for the given expression kind are always 437 /// kept around. 438 static bool isInterestingLValueExpr(const Expr *Ex); 439 440 private: 441 bool shouldCollect(const ExplodedNode *node); 442 void collectNode(ExplodedNode *node); 443 }; 444 445 class ExplodedNodeSet { 446 using ImplTy = llvm::SmallSetVector<ExplodedNode *, 4>; 447 ImplTy Impl; 448 449 public: ExplodedNodeSet(ExplodedNode * N)450 ExplodedNodeSet(ExplodedNode *N) { 451 assert(N && !N->isSink()); 452 Impl.insert(N); 453 } 454 455 ExplodedNodeSet() = default; 456 Add(ExplodedNode * N)457 void Add(ExplodedNode *N) { 458 if (N && !N->isSink()) 459 Impl.insert(N); 460 } 461 462 using iterator = ImplTy::iterator; 463 using const_iterator = ImplTy::const_iterator; 464 size()465 unsigned size() const { return Impl.size(); } empty()466 bool empty() const { return Impl.empty(); } erase(ExplodedNode * N)467 bool erase(ExplodedNode *N) { return Impl.remove(N); } 468 clear()469 void clear() { Impl.clear(); } 470 insert(const ExplodedNodeSet & S)471 void insert(const ExplodedNodeSet &S) { 472 assert(&S != this); 473 if (empty()) 474 Impl = S.Impl; 475 else 476 Impl.insert_range(S); 477 } 478 begin()479 iterator begin() { return Impl.begin(); } end()480 iterator end() { return Impl.end(); } 481 begin()482 const_iterator begin() const { return Impl.begin(); } end()483 const_iterator end() const { return Impl.end(); } 484 }; 485 486 } // namespace ento 487 488 } // namespace clang 489 490 // GraphTraits 491 492 namespace llvm { 493 template <> struct GraphTraits<clang::ento::ExplodedGraph *> { 494 using GraphTy = clang::ento::ExplodedGraph *; 495 using NodeRef = clang::ento::ExplodedNode *; 496 using ChildIteratorType = clang::ento::ExplodedNode::succ_iterator; 497 using nodes_iterator = llvm::df_iterator<GraphTy>; 498 499 static NodeRef getEntryNode(const GraphTy G) { return G->getRoot(); } 500 501 static bool predecessorOfTrivial(NodeRef N) { 502 return N->succ_size() == 1 && N->getFirstSucc()->isTrivial(); 503 } 504 505 static ChildIteratorType child_begin(NodeRef N) { 506 if (predecessorOfTrivial(N)) 507 return child_begin(*N->succ_begin()); 508 return N->succ_begin(); 509 } 510 511 static ChildIteratorType child_end(NodeRef N) { 512 if (predecessorOfTrivial(N)) 513 return child_end(N->getFirstSucc()); 514 return N->succ_end(); 515 } 516 517 static nodes_iterator nodes_begin(const GraphTy G) { 518 return df_begin(G); 519 } 520 521 static nodes_iterator nodes_end(const GraphTy G) { 522 return df_end(G); 523 } 524 }; 525 } // namespace llvm 526 527 #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H 528