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