Home | History | Annotate | Line # | Download | only in BugReporter
      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;
     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 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)
    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:
    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:
    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:
    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:
    300   static void *getTag() {
    301     static int Tag = 0;
    302     return static_cast<void *>(&Tag);
    303   }
    304 
    305   void Profile(llvm::FoldingSetNodeID &ID) const override {
    306     ID.AddPointer(getTag());
    307   }
    308 
    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:
    328   UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {}
    329 
    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