xref: /freebsd/contrib/llvm-project/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===- UnsafeBufferUsage.h - Replace pointers with modern C++ ---*- 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 an analysis that aids replacing buffer accesses through
10 //  raw pointers with safer C++ abstractions such as containers and views/spans.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
15 #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
16 
17 #include "clang/AST/Decl.h"
18 #include "clang/AST/Stmt.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "llvm/Support/Debug.h"
21 
22 namespace clang {
23 
24 using VarGrpTy = std::vector<const VarDecl *>;
25 using VarGrpRef = ArrayRef<const VarDecl *>;
26 
27 class VariableGroupsManager {
28 public:
29   VariableGroupsManager() = default;
30   virtual ~VariableGroupsManager() = default;
31   /// Returns the set of variables (including `Var`) that need to be fixed
32   /// together in one step.
33   ///
34   /// `Var` must be a variable that needs fix (so it must be in a group).
35   /// `HasParm` is an optional argument that will be set to true if the set of
36   /// variables, where `Var` is in, contains parameters.
37   virtual VarGrpRef getGroupOfVar(const VarDecl *Var,
38                                   bool *HasParm = nullptr) const =0;
39 
40   /// Returns the non-empty group of variables that include parameters of the
41   /// analyzing function, if such a group exists.  An empty group, otherwise.
42   virtual VarGrpRef getGroupOfParms() const =0;
43 };
44 
45 // FixitStrategy is a map from variables to the way we plan to emit fixes for
46 // these variables. It is figured out gradually by trying different fixes
47 // for different variables depending on gadgets in which these variables
48 // participate.
49 class FixitStrategy {
50 public:
51   enum class Kind {
52     Wontfix,  // We don't plan to emit a fixit for this variable.
53     Span,     // We recommend replacing the variable with std::span.
54     Iterator, // We recommend replacing the variable with std::span::iterator.
55     Array,    // We recommend replacing the variable with std::array.
56     Vector    // We recommend replacing the variable with std::vector.
57   };
58 
59 private:
60   using MapTy = llvm::DenseMap<const VarDecl *, Kind>;
61 
62   MapTy Map;
63 
64 public:
65   FixitStrategy() = default;
66   FixitStrategy(const FixitStrategy &) = delete; // Let's avoid copies.
67   FixitStrategy &operator=(const FixitStrategy &) = delete;
68   FixitStrategy(FixitStrategy &&) = default;
69   FixitStrategy &operator=(FixitStrategy &&) = default;
70 
set(const VarDecl * VD,Kind K)71   void set(const VarDecl *VD, Kind K) { Map[VD] = K; }
72 
lookup(const VarDecl * VD)73   Kind lookup(const VarDecl *VD) const {
74     auto I = Map.find(VD);
75     if (I == Map.end())
76       return Kind::Wontfix;
77 
78     return I->second;
79   }
80 };
81 
82 /// The interface that lets the caller handle unsafe buffer usage analysis
83 /// results by overriding this class's handle... methods.
84 class UnsafeBufferUsageHandler {
85 #ifndef NDEBUG
86 public:
87   // A self-debugging facility that you can use to notify the user when
88   // suggestions or fixits are incomplete.
89   // Uses std::function to avoid computing the message when it won't
90   // actually be displayed.
91   using DebugNote = std::pair<SourceLocation, std::string>;
92   using DebugNoteList = std::vector<DebugNote>;
93   using DebugNoteByVar = std::map<const VarDecl *, DebugNoteList>;
94   DebugNoteByVar DebugNotesByVar;
95 #endif
96 
97 public:
98   UnsafeBufferUsageHandler() = default;
99   virtual ~UnsafeBufferUsageHandler() = default;
100 
101   /// This analyses produces large fixits that are organized into lists
102   /// of primitive fixits (individual insertions/removals/replacements).
103   using FixItList = llvm::SmallVectorImpl<FixItHint>;
104 
105   /// Invoked when an unsafe operation over raw pointers is found.
106   virtual void handleUnsafeOperation(const Stmt *Operation,
107                                      bool IsRelatedToDecl, ASTContext &Ctx) = 0;
108 
109   /// Invoked when an unsafe operation with a std container is found.
110   virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
111                                                 bool IsRelatedToDecl,
112                                                 ASTContext &Ctx) = 0;
113 
114   /// Invoked when a fix is suggested against a variable. This function groups
115   /// all variables that must be fixed together (i.e their types must be changed
116   /// to the same target type to prevent type mismatches) into a single fixit.
117   ///
118   /// `D` is the declaration of the callable under analysis that owns `Variable`
119   /// and all of its group mates.
120   virtual void
121   handleUnsafeVariableGroup(const VarDecl *Variable,
122                             const VariableGroupsManager &VarGrpMgr,
123                             FixItList &&Fixes, const Decl *D,
124                             const FixitStrategy &VarTargetTypes) = 0;
125 
126 #ifndef NDEBUG
127 public:
areDebugNotesRequested()128   bool areDebugNotesRequested() {
129     DEBUG_WITH_TYPE("SafeBuffers", return true);
130     return false;
131   }
132 
addDebugNoteForVar(const VarDecl * VD,SourceLocation Loc,std::string Text)133   void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc,
134                           std::string Text) {
135     if (areDebugNotesRequested())
136       DebugNotesByVar[VD].push_back(std::make_pair(Loc, Text));
137   }
138 
clearDebugNotes()139   void clearDebugNotes() {
140     if (areDebugNotesRequested())
141       DebugNotesByVar.clear();
142   }
143 #endif
144 
145 public:
146   /// \return true iff buffer safety is opt-out at `Loc`; false otherwise.
147   virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0;
148 
149   /// \return true iff unsafe uses in containers should NOT be reported at
150   /// `Loc`; false otherwise.
151   virtual bool
152   ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0;
153 
154   virtual std::string
155   getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc,
156                                       StringRef WSSuffix = "") const = 0;
157 };
158 
159 // This function invokes the analysis and allows the caller to react to it
160 // through the handler class.
161 void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler,
162                             bool EmitSuggestions);
163 
164 namespace internal {
165 // Tests if any two `FixItHint`s in `FixIts` conflict.  Two `FixItHint`s
166 // conflict if they have overlapping source ranges.
167 bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
168                  const SourceManager &SM);
169 } // namespace internal
170 } // end namespace clang
171 
172 #endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
173