1 //===----- UninitializedObject.h ---------------------------------*- 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 helper classes for UninitializedObjectChecker and 10 // documentation about the logic of it. 11 // 12 // The checker reports uninitialized fields in objects created after a 13 // constructor call. 14 // 15 // This checker has several options: 16 // - "Pedantic" (boolean). If its not set or is set to false, the checker 17 // won't emit warnings for objects that don't have at least one initialized 18 // field. This may be set with 19 // 20 // `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`. 21 // 22 // - "NotesAsWarnings" (boolean). If set to true, the checker will emit a 23 // warning for each uninitialized field, as opposed to emitting one warning 24 // per constructor call, and listing the uninitialized fields that belongs 25 // to it in notes. Defaults to false. 26 // 27 // `-analyzer-config \ 28 // alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`. 29 // 30 // - "CheckPointeeInitialization" (boolean). If set to false, the checker will 31 // not analyze the pointee of pointer/reference fields, and will only check 32 // whether the object itself is initialized. Defaults to false. 33 // 34 // `-analyzer-config \ 35 // alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`. 36 // 37 // - "IgnoreRecordsWithField" (string). If supplied, the checker will not 38 // analyze structures that have a field with a name or type name that 39 // matches the given pattern. Defaults to "". 40 // 41 // `-analyzer-config \ 42 // alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`. 43 // 44 // TODO: With some clever heuristics, some pointers should be dereferenced 45 // by default. For example, if the pointee is constructed within the 46 // constructor call, it's reasonable to say that no external object 47 // references it, and we wouldn't generate multiple report on the same 48 // pointee. 49 // 50 // Most of the following methods as well as the checker itself is defined in 51 // UninitializedObjectChecker.cpp. 52 // 53 // Some methods are implemented in UninitializedPointee.cpp, to reduce the 54 // complexity of the main checker file. 55 // 56 //===----------------------------------------------------------------------===// 57 58 #ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 59 #define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 60 61 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 62 63 namespace clang { 64 namespace ento { 65 66 struct UninitObjCheckerOptions { 67 bool IsPedantic = false; 68 bool ShouldConvertNotesToWarnings = false; 69 bool CheckPointeeInitialization = false; 70 std::string IgnoredRecordsWithFieldPattern; 71 }; 72 73 /// A lightweight polymorphic wrapper around FieldRegion *. We'll use this 74 /// interface to store addinitional information about fields. As described 75 /// later, a list of these objects (i.e. "fieldchain") will be constructed and 76 /// used for printing note messages should an uninitialized value be found. 77 class FieldNode { 78 protected: 79 const FieldRegion *FR; 80 81 /// FieldNodes are never meant to be created on the heap, see 82 /// FindUninitializedFields::addFieldToUninits(). 83 /* non-virtual */ ~FieldNode() = default; 84 85 public: 86 FieldNode(const FieldRegion *FR) : FR(FR) {} 87 88 // We'll delete all of these special member functions to force the users of 89 // this interface to only store references to FieldNode objects in containers. 90 FieldNode() = delete; 91 FieldNode(const FieldNode &) = delete; 92 FieldNode(FieldNode &&) = delete; 93 FieldNode &operator=(const FieldNode &) = delete; 94 FieldNode &operator=(const FieldNode &&) = delete; 95 96 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(this); } 97 98 /// Helper method for uniqueing. 99 bool isSameRegion(const FieldRegion *OtherFR) const { 100 // Special FieldNode descendants may wrap nullpointers (for example if they 101 // describe a special relationship between two elements of the fieldchain) 102 // -- we wouldn't like to unique these objects. 103 if (FR == nullptr) 104 return false; 105 106 return FR == OtherFR; 107 } 108 109 const FieldRegion *getRegion() const { return FR; } 110 const FieldDecl *getDecl() const { 111 assert(FR); 112 return FR->getDecl(); 113 } 114 115 // When a fieldchain is printed, it will have the following format (without 116 // newline, indices are in order of insertion, from 1 to n): 117 // 118 // <note_message_n>'<prefix_n><prefix_n-1>...<prefix_1> 119 // this-><node_1><separator_1><node_2><separator_2>...<node_n>' 120 121 /// If this is the last element of the fieldchain, this method will print the 122 /// note message associated with it. 123 /// The note message should state something like "uninitialized field" or 124 /// "uninitialized pointee" etc. 125 virtual void printNoteMsg(llvm::raw_ostream &Out) const = 0; 126 127 /// Print any prefixes before the fieldchain. Could contain casts, etc. 128 virtual void printPrefix(llvm::raw_ostream &Out) const = 0; 129 130 /// Print the node. Should contain the name of the field stored in FR. 131 virtual void printNode(llvm::raw_ostream &Out) const = 0; 132 133 /// Print the separator. For example, fields may be separated with '.' or 134 /// "->". 135 virtual void printSeparator(llvm::raw_ostream &Out) const = 0; 136 137 virtual bool isBase() const { return false; } 138 }; 139 140 /// Returns with Field's name. This is a helper function to get the correct name 141 /// even if Field is a captured lambda variable. 142 std::string getVariableName(const FieldDecl *Field); 143 144 /// Represents a field chain. A field chain is a list of fields where the first 145 /// element of the chain is the object under checking (not stored), and every 146 /// other element is a field, and the element that precedes it is the object 147 /// that contains it. 148 /// 149 /// Note that this class is immutable (essentially a wrapper around an 150 /// ImmutableList), new FieldChainInfo objects may be created by member 151 /// functions such as add() and replaceHead(). 152 class FieldChainInfo { 153 public: 154 using FieldChain = llvm::ImmutableList<const FieldNode &>; 155 156 private: 157 FieldChain::Factory &ChainFactory; 158 FieldChain Chain; 159 160 FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain) 161 : FieldChainInfo(F) { 162 Chain = NewChain; 163 } 164 165 public: 166 FieldChainInfo() = delete; 167 FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {} 168 FieldChainInfo(const FieldChainInfo &Other) = default; 169 170 /// Constructs a new FieldChainInfo object with \p FN appended. 171 template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN); 172 173 /// Constructs a new FieldChainInfo object with \p FN as the new head of the 174 /// list. 175 template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN); 176 177 bool contains(const FieldRegion *FR) const; 178 bool isEmpty() const { return Chain.isEmpty(); } 179 180 const FieldNode &getHead() const { return Chain.getHead(); } 181 const FieldRegion *getUninitRegion() const { return getHead().getRegion(); } 182 183 void printNoteMsg(llvm::raw_ostream &Out) const; 184 }; 185 186 using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>; 187 188 /// Searches for and stores uninitialized fields in a non-union object. 189 class FindUninitializedFields { 190 ProgramStateRef State; 191 const TypedValueRegion *const ObjectR; 192 193 const UninitObjCheckerOptions Opts; 194 bool IsAnyFieldInitialized = false; 195 196 FieldChainInfo::FieldChain::Factory ChainFactory; 197 198 /// A map for assigning uninitialized regions to note messages. For example, 199 /// 200 /// struct A { 201 /// int x; 202 /// }; 203 /// 204 /// A a; 205 /// 206 /// After analyzing `a`, the map will contain a pair for `a.x`'s region and 207 /// the note message "uninitialized field 'this->x'. 208 UninitFieldMap UninitFields; 209 210 public: 211 /// Constructs the FindUninitializedField object, searches for and stores 212 /// uninitialized fields in R. 213 FindUninitializedFields(ProgramStateRef State, 214 const TypedValueRegion *const R, 215 const UninitObjCheckerOptions &Opts); 216 217 /// Returns with the modified state and a map of (uninitialized region, 218 /// note message) pairs. 219 std::pair<ProgramStateRef, const UninitFieldMap &> getResults() { 220 return {State, UninitFields}; 221 } 222 223 /// Returns whether the analyzed region contains at least one initialized 224 /// field. Note that this includes subfields as well, not just direct ones, 225 /// and will return false if an uninitialized pointee is found with 226 /// CheckPointeeInitialization enabled. 227 bool isAnyFieldInitialized() { return IsAnyFieldInitialized; } 228 229 private: 230 // For the purposes of this checker, we'll regard the analyzed region as a 231 // directed tree, where 232 // * the root is the object under checking 233 // * every node is an object that is 234 // - a union 235 // - a non-union record 236 // - dereferenceable (see isDereferencableType()) 237 // - an array 238 // - of a primitive type (see isPrimitiveType()) 239 // * the parent of each node is the object that contains it 240 // * every leaf is an array, a primitive object, a nullptr or an undefined 241 // pointer. 242 // 243 // Example: 244 // 245 // struct A { 246 // struct B { 247 // int x, y = 0; 248 // }; 249 // B b; 250 // int *iptr = new int; 251 // B* bptr; 252 // 253 // A() {} 254 // }; 255 // 256 // The directed tree: 257 // 258 // ->x 259 // / 260 // ->b--->y 261 // / 262 // A-->iptr->(int value) 263 // \ 264 // ->bptr 265 // 266 // From this we'll construct a vector of fieldchains, where each fieldchain 267 // represents an uninitialized field. An uninitialized field may be a 268 // primitive object, a pointer, a pointee or a union without a single 269 // initialized field. 270 // In the above example, for the default constructor call we'll end up with 271 // these fieldchains: 272 // 273 // this->b.x 274 // this->iptr (pointee uninit) 275 // this->bptr (pointer uninit) 276 // 277 // We'll traverse each node of the above graph with the appropriate one of 278 // these methods: 279 280 /// Checks the region of a union object, and returns true if no field is 281 /// initialized within the region. 282 bool isUnionUninit(const TypedValueRegion *R); 283 284 /// Checks a region of a non-union object, and returns true if an 285 /// uninitialized field is found within the region. 286 bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); 287 288 /// Checks a region of a pointer or reference object, and returns true if the 289 /// ptr/ref object itself or any field within the pointee's region is 290 /// uninitialized. 291 bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain); 292 293 /// Returns true if the value of a primitive object is uninitialized. 294 bool isPrimitiveUninit(const SVal &V); 295 296 // Note that we don't have a method for arrays -- the elements of an array are 297 // often left uninitialized intentionally even when it is of a C++ record 298 // type, so we'll assume that an array is always initialized. 299 // TODO: Add a support for nonloc::LocAsInteger. 300 301 /// Processes LocalChain and attempts to insert it into UninitFields. Returns 302 /// true on success. Also adds the head of the list and \p PointeeR (if 303 /// supplied) to the GDM as already analyzed objects. 304 /// 305 /// Since this class analyzes regions with recursion, we'll only store 306 /// references to temporary FieldNode objects created on the stack. This means 307 /// that after analyzing a leaf of the directed tree described above, the 308 /// elements LocalChain references will be destructed, so we can't store it 309 /// directly. 310 bool addFieldToUninits(FieldChainInfo LocalChain, 311 const MemRegion *PointeeR = nullptr); 312 }; 313 314 /// Returns true if T is a primitive type. An object of a primitive type only 315 /// needs to be analyzed as much as checking whether their value is undefined. 316 inline bool isPrimitiveType(const QualType &T) { 317 return T->isBuiltinType() || T->isEnumeralType() || 318 T->isMemberPointerType() || T->isBlockPointerType() || 319 T->isFunctionType(); 320 } 321 322 inline bool isDereferencableType(const QualType &T) { 323 return T->isAnyPointerType() || T->isReferenceType(); 324 } 325 326 // Template method definitions. 327 328 template <class FieldNodeT> 329 inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) { 330 assert(!contains(FN.getRegion()) && 331 "Can't add a field that is already a part of the " 332 "fieldchain! Is this a cyclic reference?"); 333 334 FieldChainInfo NewChain = *this; 335 NewChain.Chain = ChainFactory.add(FN, Chain); 336 return NewChain; 337 } 338 339 template <class FieldNodeT> 340 inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) { 341 FieldChainInfo NewChain(ChainFactory, Chain.getTail()); 342 return NewChain.add(FN); 343 } 344 345 } // end of namespace ento 346 } // end of namespace clang 347 348 #endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 349