1 //===----- UninitializedObjectChecker.cpp ------------------------*- 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 a checker that reports uninitialized fields in objects 11 // created after a constructor call. 12 // 13 // This checker has several options: 14 // - "Pedantic" (boolean). If its not set or is set to false, the checker 15 // won't emit warnings for objects that don't have at least one initialized 16 // field. This may be set with 17 // 18 // `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`. 19 // 20 // - "NotesAsWarnings" (boolean). If set to true, the checker will emit a 21 // warning for each uninitalized field, as opposed to emitting one warning 22 // per constructor call, and listing the uninitialized fields that belongs 23 // to it in notes. Defaults to false. 24 // 25 // `-analyzer-config \ 26 // alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`. 27 // 28 // - "CheckPointeeInitialization" (boolean). If set to false, the checker will 29 // not analyze the pointee of pointer/reference fields, and will only check 30 // whether the object itself is initialized. Defaults to false. 31 // 32 // `-analyzer-config \ 33 // alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`. 34 // 35 // TODO: With some clever heuristics, some pointers should be dereferenced 36 // by default. For example, if the pointee is constructed within the 37 // constructor call, it's reasonable to say that no external object 38 // references it, and we wouldn't generate multiple report on the same 39 // pointee. 40 // 41 // To read about how the checker works, refer to the comments in 42 // UninitializedObject.h. 43 // 44 // Some of the logic is implemented in UninitializedPointee.cpp, to reduce the 45 // complexity of this file. 46 // 47 //===----------------------------------------------------------------------===// 48 49 #include "../ClangSACheckers.h" 50 #include "UninitializedObject.h" 51 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 52 #include "clang/StaticAnalyzer/Core/Checker.h" 53 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 54 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" 55 56 using namespace clang; 57 using namespace clang::ento; 58 59 namespace { 60 61 class UninitializedObjectChecker : public Checker<check::EndFunction> { 62 std::unique_ptr<BuiltinBug> BT_uninitField; 63 64 public: 65 // These fields will be initialized when registering the checker. 66 bool IsPedantic; 67 bool ShouldConvertNotesToWarnings; 68 bool CheckPointeeInitialization; 69 70 UninitializedObjectChecker() 71 : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} 72 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 73 }; 74 75 /// A basic field type, that is not a pointer or a reference, it's dynamic and 76 /// static type is the same. 77 class RegularField final : public FieldNode { 78 public: 79 RegularField(const FieldRegion *FR) : FieldNode(FR) {} 80 81 virtual void printNoteMsg(llvm::raw_ostream &Out) const override { 82 Out << "uninitialized field "; 83 } 84 85 virtual void printPrefix(llvm::raw_ostream &Out) const override {} 86 87 virtual void printNode(llvm::raw_ostream &Out) const override { 88 Out << getVariableName(getDecl()); 89 } 90 91 virtual void printSeparator(llvm::raw_ostream &Out) const override { 92 Out << '.'; 93 } 94 }; 95 96 } // end of anonymous namespace 97 98 // Utility function declarations. 99 100 /// Returns the object that was constructed by CtorDecl, or None if that isn't 101 /// possible. 102 // TODO: Refactor this function so that it returns the constructed object's 103 // region. 104 static Optional<nonloc::LazyCompoundVal> 105 getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context); 106 107 /// Checks whether the object constructed by \p Ctor will be analyzed later 108 /// (e.g. if the object is a field of another object, in which case we'd check 109 /// it multiple times). 110 static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, 111 CheckerContext &Context); 112 113 //===----------------------------------------------------------------------===// 114 // Methods for UninitializedObjectChecker. 115 //===----------------------------------------------------------------------===// 116 117 void UninitializedObjectChecker::checkEndFunction( 118 const ReturnStmt *RS, CheckerContext &Context) const { 119 120 const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>( 121 Context.getLocationContext()->getDecl()); 122 if (!CtorDecl) 123 return; 124 125 if (!CtorDecl->isUserProvided()) 126 return; 127 128 if (CtorDecl->getParent()->isUnion()) 129 return; 130 131 // This avoids essentially the same error being reported multiple times. 132 if (willObjectBeAnalyzedLater(CtorDecl, Context)) 133 return; 134 135 Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context); 136 if (!Object) 137 return; 138 139 FindUninitializedFields F(Context.getState(), Object->getRegion(), 140 CheckPointeeInitialization); 141 142 const UninitFieldMap &UninitFields = F.getUninitFields(); 143 144 if (UninitFields.empty()) 145 return; 146 147 // In non-pedantic mode, if Object's region doesn't contain a single 148 // initialized field, we'll assume that Object was intentionally left 149 // uninitialized. 150 if (!IsPedantic && !F.isAnyFieldInitialized()) 151 return; 152 153 // There are uninitialized fields in the record. 154 155 ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState()); 156 if (!Node) 157 return; 158 159 PathDiagnosticLocation LocUsedForUniqueing; 160 const Stmt *CallSite = Context.getStackFrame()->getCallSite(); 161 if (CallSite) 162 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 163 CallSite, Context.getSourceManager(), Node->getLocationContext()); 164 165 // For Plist consumers that don't support notes just yet, we'll convert notes 166 // to warnings. 167 if (ShouldConvertNotesToWarnings) { 168 for (const auto &Pair : UninitFields) { 169 170 auto Report = llvm::make_unique<BugReport>( 171 *BT_uninitField, Pair.second, Node, LocUsedForUniqueing, 172 Node->getLocationContext()->getDecl()); 173 Context.emitReport(std::move(Report)); 174 } 175 return; 176 } 177 178 SmallString<100> WarningBuf; 179 llvm::raw_svector_ostream WarningOS(WarningBuf); 180 WarningOS << UninitFields.size() << " uninitialized field" 181 << (UninitFields.size() == 1 ? "" : "s") 182 << " at the end of the constructor call"; 183 184 auto Report = llvm::make_unique<BugReport>( 185 *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, 186 Node->getLocationContext()->getDecl()); 187 188 for (const auto &Pair : UninitFields) { 189 Report->addNote(Pair.second, 190 PathDiagnosticLocation::create(Pair.first->getDecl(), 191 Context.getSourceManager())); 192 } 193 Context.emitReport(std::move(Report)); 194 } 195 196 //===----------------------------------------------------------------------===// 197 // Methods for FindUninitializedFields. 198 //===----------------------------------------------------------------------===// 199 200 FindUninitializedFields::FindUninitializedFields( 201 ProgramStateRef State, const TypedValueRegion *const R, 202 bool CheckPointeeInitialization) 203 : State(State), ObjectR(R), 204 CheckPointeeInitialization(CheckPointeeInitialization) { 205 206 isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory)); 207 } 208 209 bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) { 210 if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( 211 Chain.getUninitRegion()->getDecl()->getLocation())) 212 return false; 213 214 UninitFieldMap::mapped_type NoteMsgBuf; 215 llvm::raw_svector_ostream OS(NoteMsgBuf); 216 Chain.printNoteMsg(OS); 217 return UninitFields 218 .insert(std::make_pair(Chain.getUninitRegion(), std::move(NoteMsgBuf))) 219 .second; 220 } 221 222 bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, 223 FieldChainInfo LocalChain) { 224 assert(R->getValueType()->isRecordType() && 225 !R->getValueType()->isUnionType() && 226 "This method only checks non-union record objects!"); 227 228 const RecordDecl *RD = 229 R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition(); 230 assert(RD && "Referred record has no definition"); 231 232 bool ContainsUninitField = false; 233 234 // Are all of this non-union's fields initialized? 235 for (const FieldDecl *I : RD->fields()) { 236 237 const auto FieldVal = 238 State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>(); 239 const auto *FR = FieldVal.getRegionAs<FieldRegion>(); 240 QualType T = I->getType(); 241 242 // If LocalChain already contains FR, then we encountered a cyclic 243 // reference. In this case, region FR is already under checking at an 244 // earlier node in the directed tree. 245 if (LocalChain.contains(FR)) 246 return false; 247 248 if (T->isStructureOrClassType()) { 249 if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR)))) 250 ContainsUninitField = true; 251 continue; 252 } 253 254 if (T->isUnionType()) { 255 if (isUnionUninit(FR)) { 256 if (addFieldToUninits(LocalChain.add(RegularField(FR)))) 257 ContainsUninitField = true; 258 } else 259 IsAnyFieldInitialized = true; 260 continue; 261 } 262 263 if (T->isArrayType()) { 264 IsAnyFieldInitialized = true; 265 continue; 266 } 267 268 if (T->isAnyPointerType() || T->isReferenceType() || 269 T->isBlockPointerType()) { 270 if (isPointerOrReferenceUninit(FR, LocalChain)) 271 ContainsUninitField = true; 272 continue; 273 } 274 275 if (isPrimitiveType(T)) { 276 SVal V = State->getSVal(FieldVal); 277 278 if (isPrimitiveUninit(V)) { 279 if (addFieldToUninits(LocalChain.add(RegularField(FR)))) 280 ContainsUninitField = true; 281 } 282 continue; 283 } 284 285 llvm_unreachable("All cases are handled!"); 286 } 287 288 // Checking bases. 289 // FIXME: As of now, because of `willObjectBeAnalyzedLater`, objects whose 290 // type is a descendant of another type will emit warnings for uninitalized 291 // inherited members. 292 // This is not the only way to analyze bases of an object -- if we didn't 293 // filter them out, and didn't analyze the bases, this checker would run for 294 // each base of the object in order of base initailization and in theory would 295 // find every uninitalized field. This approach could also make handling 296 // diamond inheritances more easily. 297 // 298 // This rule (that a descendant type's cunstructor is responsible for 299 // initializing inherited data members) is not obvious, and should it should 300 // be. 301 const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); 302 if (!CXXRD) 303 return ContainsUninitField; 304 305 for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) { 306 const auto *BaseRegion = State->getLValue(BaseSpec, R) 307 .castAs<loc::MemRegionVal>() 308 .getRegionAs<TypedValueRegion>(); 309 310 if (isNonUnionUninit(BaseRegion, LocalChain)) 311 ContainsUninitField = true; 312 } 313 314 return ContainsUninitField; 315 } 316 317 bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { 318 assert(R->getValueType()->isUnionType() && 319 "This method only checks union objects!"); 320 // TODO: Implement support for union fields. 321 return false; 322 } 323 324 bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) { 325 if (V.isUndef()) 326 return true; 327 328 IsAnyFieldInitialized = true; 329 return false; 330 } 331 332 //===----------------------------------------------------------------------===// 333 // Methods for FieldChainInfo. 334 //===----------------------------------------------------------------------===// 335 336 const FieldRegion *FieldChainInfo::getUninitRegion() const { 337 assert(!Chain.isEmpty() && "Empty fieldchain!"); 338 return (*Chain.begin()).getRegion(); 339 } 340 341 bool FieldChainInfo::contains(const FieldRegion *FR) const { 342 for (const FieldNode &Node : Chain) { 343 if (Node.isSameRegion(FR)) 344 return true; 345 } 346 return false; 347 } 348 349 /// Prints every element except the last to `Out`. Since ImmutableLists store 350 /// elements in reverse order, and have no reverse iterators, we use a 351 /// recursive function to print the fieldchain correctly. The last element in 352 /// the chain is to be printed by `print`. 353 static void printTail(llvm::raw_ostream &Out, 354 const FieldChainInfo::FieldChainImpl *L); 355 356 // TODO: This function constructs an incorrect string if a void pointer is a 357 // part of the chain: 358 // 359 // struct B { int x; } 360 // 361 // struct A { 362 // void *vptr; 363 // A(void* vptr) : vptr(vptr) {} 364 // }; 365 // 366 // void f() { 367 // B b; 368 // A a(&b); 369 // } 370 // 371 // The note message will be "uninitialized field 'this->vptr->x'", even though 372 // void pointers can't be dereferenced. This should be changed to "uninitialized 373 // field 'static_cast<B*>(this->vptr)->x'". 374 // 375 // TODO: This function constructs an incorrect fieldchain string in the 376 // following case: 377 // 378 // struct Base { int x; }; 379 // struct D1 : Base {}; struct D2 : Base {}; 380 // 381 // struct MostDerived : D1, D2 { 382 // MostDerived() {} 383 // } 384 // 385 // A call to MostDerived::MostDerived() will cause two notes that say 386 // "uninitialized field 'this->x'", but we can't refer to 'x' directly, 387 // we need an explicit namespace resolution whether the uninit field was 388 // 'D1::x' or 'D2::x'. 389 void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const { 390 if (Chain.isEmpty()) 391 return; 392 393 const FieldChainImpl *L = Chain.getInternalPointer(); 394 const FieldNode &LastField = L->getHead(); 395 396 LastField.printNoteMsg(Out); 397 Out << '\''; 398 399 for (const FieldNode &Node : Chain) 400 Node.printPrefix(Out); 401 402 Out << "this->"; 403 printTail(Out, L->getTail()); 404 LastField.printNode(Out); 405 Out << '\''; 406 } 407 408 static void printTail(llvm::raw_ostream &Out, 409 const FieldChainInfo::FieldChainImpl *L) { 410 if (!L) 411 return; 412 413 printTail(Out, L->getTail()); 414 415 L->getHead().printNode(Out); 416 L->getHead().printSeparator(Out); 417 } 418 419 //===----------------------------------------------------------------------===// 420 // Utility functions. 421 //===----------------------------------------------------------------------===// 422 423 static Optional<nonloc::LazyCompoundVal> 424 getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) { 425 426 Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(), 427 Context.getStackFrame()); 428 // Getting the value for 'this'. 429 SVal This = Context.getState()->getSVal(ThisLoc); 430 431 // Getting the value for '*this'. 432 SVal Object = Context.getState()->getSVal(This.castAs<Loc>()); 433 434 return Object.getAs<nonloc::LazyCompoundVal>(); 435 } 436 437 static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, 438 CheckerContext &Context) { 439 440 Optional<nonloc::LazyCompoundVal> CurrentObject = getObjectVal(Ctor, Context); 441 if (!CurrentObject) 442 return false; 443 444 const LocationContext *LC = Context.getLocationContext(); 445 while ((LC = LC->getParent())) { 446 447 // If \p Ctor was called by another constructor. 448 const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl()); 449 if (!OtherCtor) 450 continue; 451 452 Optional<nonloc::LazyCompoundVal> OtherObject = 453 getObjectVal(OtherCtor, Context); 454 if (!OtherObject) 455 continue; 456 457 // If the CurrentObject is a subregion of OtherObject, it will be analyzed 458 // during the analysis of OtherObject. 459 if (CurrentObject->getRegion()->isSubRegionOf(OtherObject->getRegion())) 460 return true; 461 } 462 463 return false; 464 } 465 466 StringRef clang::ento::getVariableName(const FieldDecl *Field) { 467 // If Field is a captured lambda variable, Field->getName() will return with 468 // an empty string. We can however acquire it's name from the lambda's 469 // captures. 470 const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent()); 471 472 if (CXXParent && CXXParent->isLambda()) { 473 assert(CXXParent->captures_begin()); 474 auto It = CXXParent->captures_begin() + Field->getFieldIndex(); 475 return It->getCapturedVar()->getName(); 476 } 477 478 return Field->getName(); 479 } 480 481 void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { 482 auto Chk = Mgr.registerChecker<UninitializedObjectChecker>(); 483 Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption( 484 "Pedantic", /*DefaultVal*/ false, Chk); 485 Chk->ShouldConvertNotesToWarnings = Mgr.getAnalyzerOptions().getBooleanOption( 486 "NotesAsWarnings", /*DefaultVal*/ false, Chk); 487 Chk->CheckPointeeInitialization = Mgr.getAnalyzerOptions().getBooleanOption( 488 "CheckPointeeInitialization", /*DefaultVal*/ false, Chk); 489 } 490