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