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