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