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 FieldChain = llvm::ImmutableList<const FieldNode &>; 156 157 private: 158 FieldChain::Factory &ChainFactory; 159 FieldChain Chain; 160 161 FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain) 162 : FieldChainInfo(F) { 163 Chain = NewChain; 164 } 165 166 public: 167 FieldChainInfo() = delete; 168 FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {} 169 FieldChainInfo(const FieldChainInfo &Other) = default; 170 171 /// Constructs a new FieldChainInfo object with \p FN appended. 172 template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN); 173 174 /// Constructs a new FieldChainInfo object with \p FN as the new head of the 175 /// list. 176 template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN); 177 178 bool contains(const FieldRegion *FR) const; 179 bool isEmpty() const { return Chain.isEmpty(); } 180 181 const FieldNode &getHead() const { return Chain.getHead(); } 182 const FieldRegion *getUninitRegion() const { return getHead().getRegion(); } 183 184 void printNoteMsg(llvm::raw_ostream &Out) const; 185 }; 186 187 using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>; 188 189 /// Searches for and stores uninitialized fields in a non-union object. 190 class FindUninitializedFields { 191 ProgramStateRef State; 192 const TypedValueRegion *const ObjectR; 193 194 const UninitObjCheckerOptions Opts; 195 bool IsAnyFieldInitialized = false; 196 197 FieldChainInfo::FieldChain::Factory ChainFactory; 198 199 /// A map for assigning uninitialized regions to note messages. For example, 200 /// 201 /// struct A { 202 /// int x; 203 /// }; 204 /// 205 /// A a; 206 /// 207 /// After analyzing `a`, the map will contain a pair for `a.x`'s region and 208 /// the note message "uninitialized field 'this->x'. 209 UninitFieldMap UninitFields; 210 211 public: 212 /// Constructs the FindUninitializedField object, searches for and stores 213 /// uninitialized fields in R. 214 FindUninitializedFields(ProgramStateRef State, 215 const TypedValueRegion *const R, 216 const UninitObjCheckerOptions &Opts); 217 218 const UninitFieldMap &getUninitFields() { return UninitFields; } 219 220 /// Returns whether the analyzed region contains at least one initialized 221 /// field. Note that this includes subfields as well, not just direct ones, 222 /// and will return false if an uninitialized pointee is found with 223 /// CheckPointeeInitialization enabled. 224 bool isAnyFieldInitialized() { return IsAnyFieldInitialized; } 225 226 private: 227 // For the purposes of this checker, we'll regard the analyzed region as a 228 // directed tree, where 229 // * the root is the object under checking 230 // * every node is an object that is 231 // - a union 232 // - a non-union record 233 // - dereferencable (see isDereferencableType()) 234 // - an array 235 // - of a primitive type (see isPrimitiveType()) 236 // * the parent of each node is the object that contains it 237 // * every leaf is an array, a primitive object, a nullptr or an undefined 238 // pointer. 239 // 240 // Example: 241 // 242 // struct A { 243 // struct B { 244 // int x, y = 0; 245 // }; 246 // B b; 247 // int *iptr = new int; 248 // B* bptr; 249 // 250 // A() {} 251 // }; 252 // 253 // The directed tree: 254 // 255 // ->x 256 // / 257 // ->b--->y 258 // / 259 // A-->iptr->(int value) 260 // \ 261 // ->bptr 262 // 263 // From this we'll construct a vector of fieldchains, where each fieldchain 264 // represents an uninitialized field. An uninitialized field may be a 265 // primitive object, a pointer, a pointee or a union without a single 266 // initialized field. 267 // In the above example, for the default constructor call we'll end up with 268 // these fieldchains: 269 // 270 // this->b.x 271 // this->iptr (pointee uninit) 272 // this->bptr (pointer uninit) 273 // 274 // We'll traverse each node of the above graph with the appropiate one of 275 // these methods: 276 277 /// Checks the region of a union object, and returns true if no field is 278 /// initialized within the region. 279 bool isUnionUninit(const TypedValueRegion *R); 280 281 /// Checks a region of a non-union object, and returns true if an 282 /// uninitialized field is found within the region. 283 bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); 284 285 /// Checks a region of a pointer or reference object, and returns true if the 286 /// ptr/ref object itself or any field within the pointee's region is 287 /// uninitialized. 288 bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain); 289 290 /// Returns true if the value of a primitive object is uninitialized. 291 bool isPrimitiveUninit(const SVal &V); 292 293 // Note that we don't have a method for arrays -- the elements of an array are 294 // often left uninitialized intentionally even when it is of a C++ record 295 // type, so we'll assume that an array is always initialized. 296 // TODO: Add a support for nonloc::LocAsInteger. 297 298 /// Processes LocalChain and attempts to insert it into UninitFields. Returns 299 /// true on success. 300 /// 301 /// Since this class analyzes regions with recursion, we'll only store 302 /// references to temporary FieldNode objects created on the stack. This means 303 /// that after analyzing a leaf of the directed tree described above, the 304 /// elements LocalChain references will be destructed, so we can't store it 305 /// directly. 306 bool addFieldToUninits(FieldChainInfo LocalChain); 307 }; 308 309 /// Returns true if T is a primitive type. An object of a primitive type only 310 /// needs to be analyzed as much as checking whether their value is undefined. 311 inline bool isPrimitiveType(const QualType &T) { 312 return T->isBuiltinType() || T->isEnumeralType() || 313 T->isMemberPointerType() || T->isBlockPointerType() || 314 T->isFunctionType(); 315 } 316 317 inline bool isDereferencableType(const QualType &T) { 318 return T->isAnyPointerType() || T->isReferenceType(); 319 } 320 321 // Template method definitions. 322 323 template <class FieldNodeT> 324 inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) { 325 assert(!contains(FN.getRegion()) && 326 "Can't add a field that is already a part of the " 327 "fieldchain! Is this a cyclic reference?"); 328 329 FieldChainInfo NewChain = *this; 330 NewChain.Chain = ChainFactory.add(FN, Chain); 331 return NewChain; 332 } 333 334 template <class FieldNodeT> 335 inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) { 336 FieldChainInfo NewChain(ChainFactory, Chain.getTail()); 337 return NewChain.add(FN); 338 } 339 340 } // end of namespace ento 341 } // end of namespace clang 342 343 #endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 344