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