1 //===- BugReporterVisitors.h - Generate PathDiagnostics ---------*- 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 declares BugReporterVisitors, which are used to generate enhanced 10 // diagnostic traces. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H 15 #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H 16 17 #include "clang/Analysis/ProgramPoint.h" 18 #include "clang/Basic/LLVM.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 21 #include "llvm/ADT/FoldingSet.h" 22 #include "llvm/ADT/STLExtras.h" 23 #include "llvm/ADT/StringRef.h" 24 #include <memory> 25 26 namespace clang { 27 28 class BinaryOperator; 29 class CFGBlock; 30 class DeclRefExpr; 31 class Expr; 32 class Stmt; 33 34 namespace ento { 35 36 class PathSensitiveBugReport; 37 class BugReporterContext; 38 class ExplodedNode; 39 class MemRegion; 40 class PathDiagnosticPiece; 41 using PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>; 42 43 /// BugReporterVisitors are used to add custom diagnostics along a path. 44 class BugReporterVisitor : public llvm::FoldingSetNode { 45 public: 46 BugReporterVisitor() = default; 47 BugReporterVisitor(const BugReporterVisitor &) = default; BugReporterVisitor(BugReporterVisitor &&)48 BugReporterVisitor(BugReporterVisitor &&) {} 49 virtual ~BugReporterVisitor(); 50 51 /// Return a diagnostic piece which should be associated with the 52 /// given node. 53 /// Note that this function does *not* get run on the very last node 54 /// of the report, as the PathDiagnosticPiece associated with the 55 /// last node should be unique. 56 /// Use \ref getEndPath to customize the note associated with the report 57 /// end instead. 58 /// 59 /// The last parameter can be used to register a new visitor with the given 60 /// BugReport while processing a node. 61 virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 62 BugReporterContext &BRC, 63 PathSensitiveBugReport &BR) = 0; 64 65 /// Last function called on the visitor, no further calls to VisitNode 66 /// would follow. 67 virtual void finalizeVisitor(BugReporterContext &BRC, 68 const ExplodedNode *EndPathNode, 69 PathSensitiveBugReport &BR); 70 71 /// Provide custom definition for the final diagnostic piece on the 72 /// path - the piece, which is displayed before the path is expanded. 73 /// 74 /// NOTE that this function can be implemented on at most one used visitor, 75 /// and otherwise it crahes at runtime. 76 virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, 77 const ExplodedNode *N, 78 PathSensitiveBugReport &BR); 79 80 virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; 81 82 /// Generates the default final diagnostic piece. 83 static PathDiagnosticPieceRef 84 getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, 85 const PathSensitiveBugReport &BR); 86 }; 87 88 namespace bugreporter { 89 90 /// Specifies the type of tracking for an expression. 91 enum class TrackingKind { 92 /// Default tracking kind -- specifies that as much information should be 93 /// gathered about the tracked expression value as possible. 94 Thorough, 95 /// Specifies that a more moderate tracking should be used for the expression 96 /// value. This will essentially make sure that functions relevant to the it 97 /// aren't pruned, but otherwise relies on the user reading the code or 98 /// following the arrows. 99 Condition 100 }; 101 102 /// Attempts to add visitors to track expression value back to its point of 103 /// origin. 104 /// 105 /// \param N A node "downstream" from the evaluation of the statement. 106 /// \param E The expression value which we are tracking 107 /// \param R The bug report to which visitors should be attached. 108 /// \param EnableNullFPSuppression Whether we should employ false positive 109 /// suppression (inlined defensive checks, returned null). 110 /// 111 /// \return Whether or not the function was able to add visitors for this 112 /// statement. Note that returning \c true does not actually imply 113 /// that any visitors were added. 114 bool trackExpressionValue(const ExplodedNode *N, const Expr *E, 115 PathSensitiveBugReport &R, 116 TrackingKind TKind = TrackingKind::Thorough, 117 bool EnableNullFPSuppression = true); 118 119 const Expr *getDerefExpr(const Stmt *S); 120 121 } // namespace bugreporter 122 123 /// Finds last store into the given region, 124 /// which is different from a given symbolic value. 125 class FindLastStoreBRVisitor final : public BugReporterVisitor { 126 const MemRegion *R; 127 SVal V; 128 bool Satisfied = false; 129 130 /// If the visitor is tracking the value directly responsible for the 131 /// bug, we are going to employ false positive suppression. 132 bool EnableNullFPSuppression; 133 134 using TrackingKind = bugreporter::TrackingKind; 135 TrackingKind TKind; 136 const StackFrameContext *OriginSFC; 137 138 public: 139 /// \param V We're searching for the store where \c R received this value. 140 /// \param R The region we're tracking. 141 /// \param TKind May limit the amount of notes added to the bug report. 142 /// \param OriginSFC Only adds notes when the last store happened in a 143 /// different stackframe to this one. Disregarded if the tracking kind 144 /// is thorough. 145 /// This is useful, because for non-tracked regions, notes about 146 /// changes to its value in a nested stackframe could be pruned, and 147 /// this visitor can prevent that without polluting the bugpath too 148 /// much. 149 FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R, 150 bool InEnableNullFPSuppression, TrackingKind TKind, 151 const StackFrameContext *OriginSFC = nullptr) R(R)152 : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression), 153 TKind(TKind), OriginSFC(OriginSFC) { 154 assert(R); 155 } 156 157 void Profile(llvm::FoldingSetNodeID &ID) const override; 158 159 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 160 BugReporterContext &BRC, 161 PathSensitiveBugReport &BR) override; 162 }; 163 164 class TrackConstraintBRVisitor final : public BugReporterVisitor { 165 DefinedSVal Constraint; 166 bool Assumption; 167 bool IsSatisfied = false; 168 bool IsZeroCheck; 169 170 /// We should start tracking from the last node along the path in which the 171 /// value is constrained. 172 bool IsTrackingTurnedOn = false; 173 174 public: TrackConstraintBRVisitor(DefinedSVal constraint,bool assumption)175 TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) 176 : Constraint(constraint), Assumption(assumption), 177 IsZeroCheck(!Assumption && Constraint.getAs<Loc>()) {} 178 179 void Profile(llvm::FoldingSetNodeID &ID) const override; 180 181 /// Return the tag associated with this visitor. This tag will be used 182 /// to make all PathDiagnosticPieces created by this visitor. 183 static const char *getTag(); 184 185 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 186 BugReporterContext &BRC, 187 PathSensitiveBugReport &BR) override; 188 189 private: 190 /// Checks if the constraint is valid in the current state. 191 bool isUnderconstrained(const ExplodedNode *N) const; 192 }; 193 194 /// \class NilReceiverBRVisitor 195 /// Prints path notes when a message is sent to a nil receiver. 196 class NilReceiverBRVisitor final : public BugReporterVisitor { 197 public: Profile(llvm::FoldingSetNodeID & ID)198 void Profile(llvm::FoldingSetNodeID &ID) const override { 199 static int x = 0; 200 ID.AddPointer(&x); 201 } 202 203 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 204 BugReporterContext &BRC, 205 PathSensitiveBugReport &BR) override; 206 207 /// If the statement is a message send expression with nil receiver, returns 208 /// the receiver expression. Returns NULL otherwise. 209 static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N); 210 }; 211 212 /// Visitor that tries to report interesting diagnostics from conditions. 213 class ConditionBRVisitor final : public BugReporterVisitor { 214 // FIXME: constexpr initialization isn't supported by MSVC2013. 215 constexpr static llvm::StringLiteral GenericTrueMessage = 216 "Assuming the condition is true"; 217 constexpr static llvm::StringLiteral GenericFalseMessage = 218 "Assuming the condition is false"; 219 220 public: Profile(llvm::FoldingSetNodeID & ID)221 void Profile(llvm::FoldingSetNodeID &ID) const override { 222 static int x = 0; 223 ID.AddPointer(&x); 224 } 225 226 /// Return the tag associated with this visitor. This tag will be used 227 /// to make all PathDiagnosticPieces created by this visitor. 228 static const char *getTag(); 229 230 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 231 BugReporterContext &BRC, 232 PathSensitiveBugReport &BR) override; 233 234 PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N, 235 BugReporterContext &BRC, 236 PathSensitiveBugReport &BR); 237 238 PathDiagnosticPieceRef 239 VisitTerminator(const Stmt *Term, const ExplodedNode *N, 240 const CFGBlock *SrcBlk, const CFGBlock *DstBlk, 241 PathSensitiveBugReport &R, BugReporterContext &BRC); 242 243 PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, 244 BugReporterContext &BRC, 245 PathSensitiveBugReport &R, 246 const ExplodedNode *N, bool TookTrue); 247 248 PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, 249 BugReporterContext &BRC, 250 PathSensitiveBugReport &R, 251 const ExplodedNode *N, bool TookTrue, 252 bool IsAssuming); 253 254 PathDiagnosticPieceRef 255 VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, 256 BugReporterContext &BRC, PathSensitiveBugReport &R, 257 const ExplodedNode *N, bool TookTrue, bool IsAssuming); 258 259 PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const MemberExpr *ME, 260 BugReporterContext &BRC, 261 PathSensitiveBugReport &R, 262 const ExplodedNode *N, bool TookTrue, 263 bool IsAssuming); 264 265 PathDiagnosticPieceRef 266 VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr, 267 BugReporterContext &BRC, PathSensitiveBugReport &R, 268 const ExplodedNode *N, bool TookTrue); 269 270 /// Tries to print the value of the given expression. 271 /// 272 /// \param CondVarExpr The expression to print its value. 273 /// \param Out The stream to print. 274 /// \param N The node where we encountered the condition. 275 /// \param TookTrue Whether we took the \c true branch of the condition. 276 /// 277 /// \return Whether the print was successful. (The printing is successful if 278 /// we model the value and we could obtain it.) 279 bool printValue(const Expr *CondVarExpr, raw_ostream &Out, 280 const ExplodedNode *N, bool TookTrue, bool IsAssuming); 281 282 bool patternMatch(const Expr *Ex, 283 const Expr *ParentEx, 284 raw_ostream &Out, 285 BugReporterContext &BRC, 286 PathSensitiveBugReport &R, 287 const ExplodedNode *N, 288 Optional<bool> &prunable, 289 bool IsSameFieldName); 290 291 static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece); 292 }; 293 294 /// Suppress reports that might lead to known false positives. 295 /// 296 /// Currently this suppresses reports based on locations of bugs. 297 class LikelyFalsePositiveSuppressionBRVisitor final 298 : public BugReporterVisitor { 299 public: getTag()300 static void *getTag() { 301 static int Tag = 0; 302 return static_cast<void *>(&Tag); 303 } 304 Profile(llvm::FoldingSetNodeID & ID)305 void Profile(llvm::FoldingSetNodeID &ID) const override { 306 ID.AddPointer(getTag()); 307 } 308 VisitNode(const ExplodedNode *,BugReporterContext &,PathSensitiveBugReport &)309 PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &, 310 PathSensitiveBugReport &) override { 311 return nullptr; 312 } 313 314 void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, 315 PathSensitiveBugReport &BR) override; 316 }; 317 318 /// When a region containing undefined value or '0' value is passed 319 /// as an argument in a call, marks the call as interesting. 320 /// 321 /// As a result, BugReporter will not prune the path through the function even 322 /// if the region's contents are not modified/accessed by the call. 323 class UndefOrNullArgVisitor final : public BugReporterVisitor { 324 /// The interesting memory region this visitor is tracking. 325 const MemRegion *R; 326 327 public: UndefOrNullArgVisitor(const MemRegion * InR)328 UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {} 329 Profile(llvm::FoldingSetNodeID & ID)330 void Profile(llvm::FoldingSetNodeID &ID) const override { 331 static int Tag = 0; 332 ID.AddPointer(&Tag); 333 ID.AddPointer(R); 334 } 335 336 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 337 BugReporterContext &BRC, 338 PathSensitiveBugReport &BR) override; 339 }; 340 341 class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { 342 /// The symbolic value for which we are tracking constraints. 343 /// This value is constrained to null in the end of path. 344 DefinedSVal V; 345 346 /// Track if we found the node where the constraint was first added. 347 bool IsSatisfied = false; 348 349 /// Since the visitors can be registered on nodes previous to the last 350 /// node in the BugReport, but the path traversal always starts with the last 351 /// node, the visitor invariant (that we start with a node in which V is null) 352 /// might not hold when node visitation starts. We are going to start tracking 353 /// from the last node in which the value is null. 354 bool IsTrackingTurnedOn = false; 355 356 public: 357 SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N); 358 359 void Profile(llvm::FoldingSetNodeID &ID) const override; 360 361 /// Return the tag associated with this visitor. This tag will be used 362 /// to make all PathDiagnosticPieces created by this visitor. 363 static const char *getTag(); 364 365 PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, 366 BugReporterContext &BRC, 367 PathSensitiveBugReport &BR) override; 368 }; 369 370 /// The bug visitor will walk all the nodes in a path and collect all the 371 /// constraints. When it reaches the root node, will create a refutation 372 /// manager and check if the constraints are satisfiable 373 class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { 374 private: 375 /// Holds the constraints in a given path 376 ConstraintMap Constraints; 377 378 public: 379 FalsePositiveRefutationBRVisitor(); 380 381 void Profile(llvm::FoldingSetNodeID &ID) const override; 382 383 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 384 BugReporterContext &BRC, 385 PathSensitiveBugReport &BR) override; 386 387 void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, 388 PathSensitiveBugReport &BR) override; 389 void addConstraints(const ExplodedNode *N, 390 bool OverwriteConstraintsOnExistingSyms); 391 }; 392 393 /// The visitor detects NoteTags and displays the event notes they contain. 394 class TagVisitor : public BugReporterVisitor { 395 public: 396 void Profile(llvm::FoldingSetNodeID &ID) const override; 397 398 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 399 BugReporterContext &BRC, 400 PathSensitiveBugReport &R) override; 401 }; 402 403 } // namespace ento 404 405 } // namespace clang 406 407 #endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H 408