10b57cec5SDimitry Andric //===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines a checker that reports uninitialized fields in objects 100b57cec5SDimitry Andric // created after a constructor call. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // To read about command line options and how the checker works, refer to the 130b57cec5SDimitry Andric // top of the file and inline comments in UninitializedObject.h. 140b57cec5SDimitry Andric // 150b57cec5SDimitry Andric // Some of the logic is implemented in UninitializedPointee.cpp, to reduce the 160b57cec5SDimitry Andric // complexity of this file. 170b57cec5SDimitry Andric // 180b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 210b57cec5SDimitry Andric #include "UninitializedObject.h" 220b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 230b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 260b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 27a7dea167SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric using namespace clang; 300b57cec5SDimitry Andric using namespace clang::ento; 310b57cec5SDimitry Andric using namespace clang::ast_matchers; 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric /// We'll mark fields (and pointee of fields) that are confirmed to be 340b57cec5SDimitry Andric /// uninitialized as already analyzed. 350b57cec5SDimitry Andric REGISTER_SET_WITH_PROGRAMSTATE(AnalyzedRegions, const MemRegion *) 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric namespace { 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric class UninitializedObjectChecker 400b57cec5SDimitry Andric : public Checker<check::EndFunction, check::DeadSymbols> { 41*647cbc5dSDimitry Andric const BugType BT_uninitField{this, "Uninitialized fields"}; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric public: 440b57cec5SDimitry Andric // The fields of this struct will be initialized when registering the checker. 450b57cec5SDimitry Andric UninitObjCheckerOptions Opts; 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 480b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 490b57cec5SDimitry Andric }; 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric /// A basic field type, that is not a pointer or a reference, it's dynamic and 520b57cec5SDimitry Andric /// static type is the same. 530b57cec5SDimitry Andric class RegularField final : public FieldNode { 540b57cec5SDimitry Andric public: 550b57cec5SDimitry Andric RegularField(const FieldRegion *FR) : FieldNode(FR) {} 560b57cec5SDimitry Andric 57972a253aSDimitry Andric void printNoteMsg(llvm::raw_ostream &Out) const override { 580b57cec5SDimitry Andric Out << "uninitialized field "; 590b57cec5SDimitry Andric } 600b57cec5SDimitry Andric 61972a253aSDimitry Andric void printPrefix(llvm::raw_ostream &Out) const override {} 620b57cec5SDimitry Andric 63972a253aSDimitry Andric void printNode(llvm::raw_ostream &Out) const override { 640b57cec5SDimitry Andric Out << getVariableName(getDecl()); 650b57cec5SDimitry Andric } 660b57cec5SDimitry Andric 67972a253aSDimitry Andric void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; } 680b57cec5SDimitry Andric }; 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric /// Represents that the FieldNode that comes after this is declared in a base 710b57cec5SDimitry Andric /// of the previous FieldNode. As such, this descendant doesn't wrap a 720b57cec5SDimitry Andric /// FieldRegion, and is purely a tool to describe a relation between two other 730b57cec5SDimitry Andric /// FieldRegion wrapping descendants. 740b57cec5SDimitry Andric class BaseClass final : public FieldNode { 750b57cec5SDimitry Andric const QualType BaseClassT; 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric public: 780b57cec5SDimitry Andric BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) { 790b57cec5SDimitry Andric assert(!T.isNull()); 800b57cec5SDimitry Andric assert(T->getAsCXXRecordDecl()); 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 83972a253aSDimitry Andric void printNoteMsg(llvm::raw_ostream &Out) const override { 840b57cec5SDimitry Andric llvm_unreachable("This node can never be the final node in the " 850b57cec5SDimitry Andric "fieldchain!"); 860b57cec5SDimitry Andric } 870b57cec5SDimitry Andric 88972a253aSDimitry Andric void printPrefix(llvm::raw_ostream &Out) const override {} 890b57cec5SDimitry Andric 90972a253aSDimitry Andric void printNode(llvm::raw_ostream &Out) const override { 910b57cec5SDimitry Andric Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::"; 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric 94972a253aSDimitry Andric void printSeparator(llvm::raw_ostream &Out) const override {} 950b57cec5SDimitry Andric 96972a253aSDimitry Andric bool isBase() const override { return true; } 970b57cec5SDimitry Andric }; 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric } // end of anonymous namespace 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric // Utility function declarations. 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric /// Returns the region that was constructed by CtorDecl, or nullptr if that 1040b57cec5SDimitry Andric /// isn't possible. 1050b57cec5SDimitry Andric static const TypedValueRegion * 1060b57cec5SDimitry Andric getConstructedRegion(const CXXConstructorDecl *CtorDecl, 1070b57cec5SDimitry Andric CheckerContext &Context); 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric /// Checks whether the object constructed by \p Ctor will be analyzed later 1100b57cec5SDimitry Andric /// (e.g. if the object is a field of another object, in which case we'd check 1110b57cec5SDimitry Andric /// it multiple times). 1120b57cec5SDimitry Andric static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, 1130b57cec5SDimitry Andric CheckerContext &Context); 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric /// Checks whether RD contains a field with a name or type name that matches 1160b57cec5SDimitry Andric /// \p Pattern. 1170b57cec5SDimitry Andric static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern); 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric /// Checks _syntactically_ whether it is possible to access FD from the record 1200b57cec5SDimitry Andric /// that contains it without a preceding assert (even if that access happens 1210b57cec5SDimitry Andric /// inside a method). This is mainly used for records that act like unions, like 1220b57cec5SDimitry Andric /// having multiple bit fields, with only a fraction being properly initialized. 1230b57cec5SDimitry Andric /// If these fields are properly guarded with asserts, this method returns 1240b57cec5SDimitry Andric /// false. 1250b57cec5SDimitry Andric /// 1260b57cec5SDimitry Andric /// Since this check is done syntactically, this method could be inaccurate. 1270b57cec5SDimitry Andric static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State); 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1300b57cec5SDimitry Andric // Methods for UninitializedObjectChecker. 1310b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric void UninitializedObjectChecker::checkEndFunction( 1340b57cec5SDimitry Andric const ReturnStmt *RS, CheckerContext &Context) const { 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>( 1370b57cec5SDimitry Andric Context.getLocationContext()->getDecl()); 1380b57cec5SDimitry Andric if (!CtorDecl) 1390b57cec5SDimitry Andric return; 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric if (!CtorDecl->isUserProvided()) 1420b57cec5SDimitry Andric return; 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric if (CtorDecl->getParent()->isUnion()) 1450b57cec5SDimitry Andric return; 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric // This avoids essentially the same error being reported multiple times. 1480b57cec5SDimitry Andric if (willObjectBeAnalyzedLater(CtorDecl, Context)) 1490b57cec5SDimitry Andric return; 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context); 1520b57cec5SDimitry Andric if (!R) 1530b57cec5SDimitry Andric return; 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric FindUninitializedFields F(Context.getState(), R, Opts); 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo = 1580b57cec5SDimitry Andric F.getResults(); 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric ProgramStateRef UpdatedState = UninitInfo.first; 1610b57cec5SDimitry Andric const UninitFieldMap &UninitFields = UninitInfo.second; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric if (UninitFields.empty()) { 1640b57cec5SDimitry Andric Context.addTransition(UpdatedState); 1650b57cec5SDimitry Andric return; 1660b57cec5SDimitry Andric } 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric // There are uninitialized fields in the record. 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState); 1710b57cec5SDimitry Andric if (!Node) 1720b57cec5SDimitry Andric return; 1730b57cec5SDimitry Andric 1740b57cec5SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing; 1750b57cec5SDimitry Andric const Stmt *CallSite = Context.getStackFrame()->getCallSite(); 1760b57cec5SDimitry Andric if (CallSite) 1770b57cec5SDimitry Andric LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 1780b57cec5SDimitry Andric CallSite, Context.getSourceManager(), Node->getLocationContext()); 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric // For Plist consumers that don't support notes just yet, we'll convert notes 1810b57cec5SDimitry Andric // to warnings. 1820b57cec5SDimitry Andric if (Opts.ShouldConvertNotesToWarnings) { 1830b57cec5SDimitry Andric for (const auto &Pair : UninitFields) { 1840b57cec5SDimitry Andric 185a7dea167SDimitry Andric auto Report = std::make_unique<PathSensitiveBugReport>( 186*647cbc5dSDimitry Andric BT_uninitField, Pair.second, Node, LocUsedForUniqueing, 1870b57cec5SDimitry Andric Node->getLocationContext()->getDecl()); 1880b57cec5SDimitry Andric Context.emitReport(std::move(Report)); 1890b57cec5SDimitry Andric } 1900b57cec5SDimitry Andric return; 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric SmallString<100> WarningBuf; 1940b57cec5SDimitry Andric llvm::raw_svector_ostream WarningOS(WarningBuf); 1950b57cec5SDimitry Andric WarningOS << UninitFields.size() << " uninitialized field" 1960b57cec5SDimitry Andric << (UninitFields.size() == 1 ? "" : "s") 1970b57cec5SDimitry Andric << " at the end of the constructor call"; 1980b57cec5SDimitry Andric 199a7dea167SDimitry Andric auto Report = std::make_unique<PathSensitiveBugReport>( 200*647cbc5dSDimitry Andric BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, 2010b57cec5SDimitry Andric Node->getLocationContext()->getDecl()); 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric for (const auto &Pair : UninitFields) { 2040b57cec5SDimitry Andric Report->addNote(Pair.second, 2050b57cec5SDimitry Andric PathDiagnosticLocation::create(Pair.first->getDecl(), 2060b57cec5SDimitry Andric Context.getSourceManager())); 2070b57cec5SDimitry Andric } 2080b57cec5SDimitry Andric Context.emitReport(std::move(Report)); 2090b57cec5SDimitry Andric } 2100b57cec5SDimitry Andric 2110b57cec5SDimitry Andric void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR, 2120b57cec5SDimitry Andric CheckerContext &C) const { 2130b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 2140b57cec5SDimitry Andric for (const MemRegion *R : State->get<AnalyzedRegions>()) { 2150b57cec5SDimitry Andric if (!SR.isLiveRegion(R)) 2160b57cec5SDimitry Andric State = State->remove<AnalyzedRegions>(R); 2170b57cec5SDimitry Andric } 2180b57cec5SDimitry Andric } 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2210b57cec5SDimitry Andric // Methods for FindUninitializedFields. 2220b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric FindUninitializedFields::FindUninitializedFields( 2250b57cec5SDimitry Andric ProgramStateRef State, const TypedValueRegion *const R, 2260b57cec5SDimitry Andric const UninitObjCheckerOptions &Opts) 2270b57cec5SDimitry Andric : State(State), ObjectR(R), Opts(Opts) { 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory)); 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric // In non-pedantic mode, if ObjectR doesn't contain a single initialized 2320b57cec5SDimitry Andric // field, we'll assume that Object was intentionally left uninitialized. 2330b57cec5SDimitry Andric if (!Opts.IsPedantic && !isAnyFieldInitialized()) 2340b57cec5SDimitry Andric UninitFields.clear(); 2350b57cec5SDimitry Andric } 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain, 2380b57cec5SDimitry Andric const MemRegion *PointeeR) { 2390b57cec5SDimitry Andric const FieldRegion *FR = Chain.getUninitRegion(); 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric assert((PointeeR || !isDereferencableType(FR->getDecl()->getType())) && 2420b57cec5SDimitry Andric "One must also pass the pointee region as a parameter for " 2430b57cec5SDimitry Andric "dereferenceable fields!"); 2440b57cec5SDimitry Andric 2450b57cec5SDimitry Andric if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( 2460b57cec5SDimitry Andric FR->getDecl()->getLocation())) 2470b57cec5SDimitry Andric return false; 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State)) 2500b57cec5SDimitry Andric return false; 2510b57cec5SDimitry Andric 2520b57cec5SDimitry Andric if (State->contains<AnalyzedRegions>(FR)) 2530b57cec5SDimitry Andric return false; 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric if (PointeeR) { 2560b57cec5SDimitry Andric if (State->contains<AnalyzedRegions>(PointeeR)) { 2570b57cec5SDimitry Andric return false; 2580b57cec5SDimitry Andric } 2590b57cec5SDimitry Andric State = State->add<AnalyzedRegions>(PointeeR); 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric State = State->add<AnalyzedRegions>(FR); 2630b57cec5SDimitry Andric 2640b57cec5SDimitry Andric UninitFieldMap::mapped_type NoteMsgBuf; 2650b57cec5SDimitry Andric llvm::raw_svector_ostream OS(NoteMsgBuf); 2660b57cec5SDimitry Andric Chain.printNoteMsg(OS); 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second; 2690b57cec5SDimitry Andric } 2700b57cec5SDimitry Andric 2710b57cec5SDimitry Andric bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, 2720b57cec5SDimitry Andric FieldChainInfo LocalChain) { 2730b57cec5SDimitry Andric assert(R->getValueType()->isRecordType() && 2740b57cec5SDimitry Andric !R->getValueType()->isUnionType() && 2750b57cec5SDimitry Andric "This method only checks non-union record objects!"); 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition(); 2780b57cec5SDimitry Andric 2790b57cec5SDimitry Andric if (!RD) { 2800b57cec5SDimitry Andric IsAnyFieldInitialized = true; 2810b57cec5SDimitry Andric return true; 2820b57cec5SDimitry Andric } 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric if (!Opts.IgnoredRecordsWithFieldPattern.empty() && 2850b57cec5SDimitry Andric shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) { 2860b57cec5SDimitry Andric IsAnyFieldInitialized = true; 2870b57cec5SDimitry Andric return false; 2880b57cec5SDimitry Andric } 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric bool ContainsUninitField = false; 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric // Are all of this non-union's fields initialized? 2930b57cec5SDimitry Andric for (const FieldDecl *I : RD->fields()) { 2940b57cec5SDimitry Andric 2950b57cec5SDimitry Andric const auto FieldVal = 2960b57cec5SDimitry Andric State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>(); 2970b57cec5SDimitry Andric const auto *FR = FieldVal.getRegionAs<FieldRegion>(); 2980b57cec5SDimitry Andric QualType T = I->getType(); 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric // If LocalChain already contains FR, then we encountered a cyclic 3010b57cec5SDimitry Andric // reference. In this case, region FR is already under checking at an 3020b57cec5SDimitry Andric // earlier node in the directed tree. 3030b57cec5SDimitry Andric if (LocalChain.contains(FR)) 3040b57cec5SDimitry Andric return false; 3050b57cec5SDimitry Andric 3060b57cec5SDimitry Andric if (T->isStructureOrClassType()) { 3070b57cec5SDimitry Andric if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR)))) 3080b57cec5SDimitry Andric ContainsUninitField = true; 3090b57cec5SDimitry Andric continue; 3100b57cec5SDimitry Andric } 3110b57cec5SDimitry Andric 3120b57cec5SDimitry Andric if (T->isUnionType()) { 3130b57cec5SDimitry Andric if (isUnionUninit(FR)) { 3140b57cec5SDimitry Andric if (addFieldToUninits(LocalChain.add(RegularField(FR)))) 3150b57cec5SDimitry Andric ContainsUninitField = true; 3160b57cec5SDimitry Andric } else 3170b57cec5SDimitry Andric IsAnyFieldInitialized = true; 3180b57cec5SDimitry Andric continue; 3190b57cec5SDimitry Andric } 3200b57cec5SDimitry Andric 3210b57cec5SDimitry Andric if (T->isArrayType()) { 3220b57cec5SDimitry Andric IsAnyFieldInitialized = true; 3230b57cec5SDimitry Andric continue; 3240b57cec5SDimitry Andric } 3250b57cec5SDimitry Andric 3260b57cec5SDimitry Andric SVal V = State->getSVal(FieldVal); 3270b57cec5SDimitry Andric 32881ad6265SDimitry Andric if (isDereferencableType(T) || isa<nonloc::LocAsInteger>(V)) { 3290b57cec5SDimitry Andric if (isDereferencableUninit(FR, LocalChain)) 3300b57cec5SDimitry Andric ContainsUninitField = true; 3310b57cec5SDimitry Andric continue; 3320b57cec5SDimitry Andric } 3330b57cec5SDimitry Andric 3340b57cec5SDimitry Andric if (isPrimitiveType(T)) { 3350b57cec5SDimitry Andric if (isPrimitiveUninit(V)) { 3360b57cec5SDimitry Andric if (addFieldToUninits(LocalChain.add(RegularField(FR)))) 3370b57cec5SDimitry Andric ContainsUninitField = true; 3380b57cec5SDimitry Andric } 3390b57cec5SDimitry Andric continue; 3400b57cec5SDimitry Andric } 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric llvm_unreachable("All cases are handled!"); 3430b57cec5SDimitry Andric } 3440b57cec5SDimitry Andric 3450b57cec5SDimitry Andric // Checking bases. The checker will regard inherited data members as direct 3460b57cec5SDimitry Andric // fields. 3470b57cec5SDimitry Andric const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); 3480b57cec5SDimitry Andric if (!CXXRD) 3490b57cec5SDimitry Andric return ContainsUninitField; 3500b57cec5SDimitry Andric 3510b57cec5SDimitry Andric for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) { 3520b57cec5SDimitry Andric const auto *BaseRegion = State->getLValue(BaseSpec, R) 3530b57cec5SDimitry Andric .castAs<loc::MemRegionVal>() 3540b57cec5SDimitry Andric .getRegionAs<TypedValueRegion>(); 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric // If the head of the list is also a BaseClass, we'll overwrite it to avoid 3570b57cec5SDimitry Andric // note messages like 'this->A::B::x'. 3580b57cec5SDimitry Andric if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()) { 3590b57cec5SDimitry Andric if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead( 3600b57cec5SDimitry Andric BaseClass(BaseSpec.getType())))) 3610b57cec5SDimitry Andric ContainsUninitField = true; 3620b57cec5SDimitry Andric } else { 3630b57cec5SDimitry Andric if (isNonUnionUninit(BaseRegion, 3640b57cec5SDimitry Andric LocalChain.add(BaseClass(BaseSpec.getType())))) 3650b57cec5SDimitry Andric ContainsUninitField = true; 3660b57cec5SDimitry Andric } 3670b57cec5SDimitry Andric } 3680b57cec5SDimitry Andric 3690b57cec5SDimitry Andric return ContainsUninitField; 3700b57cec5SDimitry Andric } 3710b57cec5SDimitry Andric 3720b57cec5SDimitry Andric bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { 3730b57cec5SDimitry Andric assert(R->getValueType()->isUnionType() && 3740b57cec5SDimitry Andric "This method only checks union objects!"); 3750b57cec5SDimitry Andric // TODO: Implement support for union fields. 3760b57cec5SDimitry Andric return false; 3770b57cec5SDimitry Andric } 3780b57cec5SDimitry Andric 379*647cbc5dSDimitry Andric bool FindUninitializedFields::isPrimitiveUninit(SVal V) { 3800b57cec5SDimitry Andric if (V.isUndef()) 3810b57cec5SDimitry Andric return true; 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric IsAnyFieldInitialized = true; 3840b57cec5SDimitry Andric return false; 3850b57cec5SDimitry Andric } 3860b57cec5SDimitry Andric 3870b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 3880b57cec5SDimitry Andric // Methods for FieldChainInfo. 3890b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 3900b57cec5SDimitry Andric 3910b57cec5SDimitry Andric bool FieldChainInfo::contains(const FieldRegion *FR) const { 3920b57cec5SDimitry Andric for (const FieldNode &Node : Chain) { 3930b57cec5SDimitry Andric if (Node.isSameRegion(FR)) 3940b57cec5SDimitry Andric return true; 3950b57cec5SDimitry Andric } 3960b57cec5SDimitry Andric return false; 3970b57cec5SDimitry Andric } 3980b57cec5SDimitry Andric 3990b57cec5SDimitry Andric /// Prints every element except the last to `Out`. Since ImmutableLists store 4000b57cec5SDimitry Andric /// elements in reverse order, and have no reverse iterators, we use a 4010b57cec5SDimitry Andric /// recursive function to print the fieldchain correctly. The last element in 4020b57cec5SDimitry Andric /// the chain is to be printed by `FieldChainInfo::print`. 4030b57cec5SDimitry Andric static void printTail(llvm::raw_ostream &Out, 4040b57cec5SDimitry Andric const FieldChainInfo::FieldChain L); 4050b57cec5SDimitry Andric 4060b57cec5SDimitry Andric // FIXME: This function constructs an incorrect string in the following case: 4070b57cec5SDimitry Andric // 4080b57cec5SDimitry Andric // struct Base { int x; }; 4090b57cec5SDimitry Andric // struct D1 : Base {}; struct D2 : Base {}; 4100b57cec5SDimitry Andric // 4110b57cec5SDimitry Andric // struct MostDerived : D1, D2 { 4120b57cec5SDimitry Andric // MostDerived() {} 4130b57cec5SDimitry Andric // } 4140b57cec5SDimitry Andric // 4150b57cec5SDimitry Andric // A call to MostDerived::MostDerived() will cause two notes that say 4160b57cec5SDimitry Andric // "uninitialized field 'this->x'", but we can't refer to 'x' directly, 4170b57cec5SDimitry Andric // we need an explicit namespace resolution whether the uninit field was 4180b57cec5SDimitry Andric // 'D1::x' or 'D2::x'. 4190b57cec5SDimitry Andric void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const { 4200b57cec5SDimitry Andric if (Chain.isEmpty()) 4210b57cec5SDimitry Andric return; 4220b57cec5SDimitry Andric 4230b57cec5SDimitry Andric const FieldNode &LastField = getHead(); 4240b57cec5SDimitry Andric 4250b57cec5SDimitry Andric LastField.printNoteMsg(Out); 4260b57cec5SDimitry Andric Out << '\''; 4270b57cec5SDimitry Andric 4280b57cec5SDimitry Andric for (const FieldNode &Node : Chain) 4290b57cec5SDimitry Andric Node.printPrefix(Out); 4300b57cec5SDimitry Andric 4310b57cec5SDimitry Andric Out << "this->"; 4320b57cec5SDimitry Andric printTail(Out, Chain.getTail()); 4330b57cec5SDimitry Andric LastField.printNode(Out); 4340b57cec5SDimitry Andric Out << '\''; 4350b57cec5SDimitry Andric } 4360b57cec5SDimitry Andric 4370b57cec5SDimitry Andric static void printTail(llvm::raw_ostream &Out, 4380b57cec5SDimitry Andric const FieldChainInfo::FieldChain L) { 4390b57cec5SDimitry Andric if (L.isEmpty()) 4400b57cec5SDimitry Andric return; 4410b57cec5SDimitry Andric 4420b57cec5SDimitry Andric printTail(Out, L.getTail()); 4430b57cec5SDimitry Andric 4440b57cec5SDimitry Andric L.getHead().printNode(Out); 4450b57cec5SDimitry Andric L.getHead().printSeparator(Out); 4460b57cec5SDimitry Andric } 4470b57cec5SDimitry Andric 4480b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 4490b57cec5SDimitry Andric // Utility functions. 4500b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 4510b57cec5SDimitry Andric 4520b57cec5SDimitry Andric static const TypedValueRegion * 4530b57cec5SDimitry Andric getConstructedRegion(const CXXConstructorDecl *CtorDecl, 4540b57cec5SDimitry Andric CheckerContext &Context) { 4550b57cec5SDimitry Andric 4560b57cec5SDimitry Andric Loc ThisLoc = 4570b57cec5SDimitry Andric Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame()); 4580b57cec5SDimitry Andric 4590b57cec5SDimitry Andric SVal ObjectV = Context.getState()->getSVal(ThisLoc); 4600b57cec5SDimitry Andric 4610b57cec5SDimitry Andric auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>(); 4620b57cec5SDimitry Andric if (R && !R->getValueType()->getAsCXXRecordDecl()) 4630b57cec5SDimitry Andric return nullptr; 4640b57cec5SDimitry Andric 4650b57cec5SDimitry Andric return R; 4660b57cec5SDimitry Andric } 4670b57cec5SDimitry Andric 4680b57cec5SDimitry Andric static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, 4690b57cec5SDimitry Andric CheckerContext &Context) { 4700b57cec5SDimitry Andric 4710b57cec5SDimitry Andric const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context); 4720b57cec5SDimitry Andric if (!CurrRegion) 4730b57cec5SDimitry Andric return false; 4740b57cec5SDimitry Andric 4750b57cec5SDimitry Andric const LocationContext *LC = Context.getLocationContext(); 4760b57cec5SDimitry Andric while ((LC = LC->getParent())) { 4770b57cec5SDimitry Andric 4780b57cec5SDimitry Andric // If \p Ctor was called by another constructor. 4790b57cec5SDimitry Andric const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl()); 4800b57cec5SDimitry Andric if (!OtherCtor) 4810b57cec5SDimitry Andric continue; 4820b57cec5SDimitry Andric 4830b57cec5SDimitry Andric const TypedValueRegion *OtherRegion = 4840b57cec5SDimitry Andric getConstructedRegion(OtherCtor, Context); 4850b57cec5SDimitry Andric if (!OtherRegion) 4860b57cec5SDimitry Andric continue; 4870b57cec5SDimitry Andric 4880b57cec5SDimitry Andric // If the CurrRegion is a subregion of OtherRegion, it will be analyzed 4890b57cec5SDimitry Andric // during the analysis of OtherRegion. 4900b57cec5SDimitry Andric if (CurrRegion->isSubRegionOf(OtherRegion)) 4910b57cec5SDimitry Andric return true; 4920b57cec5SDimitry Andric } 4930b57cec5SDimitry Andric 4940b57cec5SDimitry Andric return false; 4950b57cec5SDimitry Andric } 4960b57cec5SDimitry Andric 4970b57cec5SDimitry Andric static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) { 4980b57cec5SDimitry Andric llvm::Regex R(Pattern); 4990b57cec5SDimitry Andric 5000b57cec5SDimitry Andric for (const FieldDecl *FD : RD->fields()) { 5010b57cec5SDimitry Andric if (R.match(FD->getType().getAsString())) 5020b57cec5SDimitry Andric return true; 5030b57cec5SDimitry Andric if (R.match(FD->getName())) 5040b57cec5SDimitry Andric return true; 5050b57cec5SDimitry Andric } 5060b57cec5SDimitry Andric 5070b57cec5SDimitry Andric return false; 5080b57cec5SDimitry Andric } 5090b57cec5SDimitry Andric 5100b57cec5SDimitry Andric static const Stmt *getMethodBody(const CXXMethodDecl *M) { 5110b57cec5SDimitry Andric if (isa<CXXConstructorDecl>(M)) 5120b57cec5SDimitry Andric return nullptr; 5130b57cec5SDimitry Andric 5140b57cec5SDimitry Andric if (!M->isDefined()) 5150b57cec5SDimitry Andric return nullptr; 5160b57cec5SDimitry Andric 5170b57cec5SDimitry Andric return M->getDefinition()->getBody(); 5180b57cec5SDimitry Andric } 5190b57cec5SDimitry Andric 5200b57cec5SDimitry Andric static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) { 5210b57cec5SDimitry Andric 5220b57cec5SDimitry Andric if (FD->getAccess() == AccessSpecifier::AS_public) 5230b57cec5SDimitry Andric return true; 5240b57cec5SDimitry Andric 5250b57cec5SDimitry Andric const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent()); 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric if (!Parent) 5280b57cec5SDimitry Andric return true; 5290b57cec5SDimitry Andric 5300b57cec5SDimitry Andric Parent = Parent->getDefinition(); 5310b57cec5SDimitry Andric assert(Parent && "The record's definition must be avaible if an uninitialized" 5320b57cec5SDimitry Andric " field of it was found!"); 5330b57cec5SDimitry Andric 5340b57cec5SDimitry Andric ASTContext &AC = State->getStateManager().getContext(); 5350b57cec5SDimitry Andric 5360b57cec5SDimitry Andric auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access"); 5370b57cec5SDimitry Andric 5380b57cec5SDimitry Andric auto AssertLikeM = callExpr(callee(functionDecl( 5395ffd83dbSDimitry Andric hasAnyName("exit", "panic", "error", "Assert", "assert", "ziperr", 5405ffd83dbSDimitry Andric "assfail", "db_error", "__assert", "__assert2", "_wassert", 5415ffd83dbSDimitry Andric "__assert_rtn", "__assert_fail", "dtrace_assfail", 5425ffd83dbSDimitry Andric "yy_fatal_error", "_XCAssertionFailureHandler", 5435ffd83dbSDimitry Andric "_DTAssertionFailureHandler", "_TSAssertionFailureHandler")))); 5440b57cec5SDimitry Andric 5450b57cec5SDimitry Andric auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn()))); 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric auto GuardM = 5480b57cec5SDimitry Andric stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM, 5490b57cec5SDimitry Andric NoReturnFuncM)) 5500b57cec5SDimitry Andric .bind("guard"); 5510b57cec5SDimitry Andric 5520b57cec5SDimitry Andric for (const CXXMethodDecl *M : Parent->methods()) { 5530b57cec5SDimitry Andric const Stmt *MethodBody = getMethodBody(M); 5540b57cec5SDimitry Andric if (!MethodBody) 5550b57cec5SDimitry Andric continue; 5560b57cec5SDimitry Andric 5570b57cec5SDimitry Andric auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC); 5580b57cec5SDimitry Andric if (Accesses.empty()) 5590b57cec5SDimitry Andric continue; 5600b57cec5SDimitry Andric const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access"); 5610b57cec5SDimitry Andric assert(FirstAccess); 5620b57cec5SDimitry Andric 5630b57cec5SDimitry Andric auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC); 5640b57cec5SDimitry Andric if (Guards.empty()) 5650b57cec5SDimitry Andric return true; 5660b57cec5SDimitry Andric const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard"); 5670b57cec5SDimitry Andric assert(FirstGuard); 5680b57cec5SDimitry Andric 5690b57cec5SDimitry Andric if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc()) 5700b57cec5SDimitry Andric return true; 5710b57cec5SDimitry Andric } 5720b57cec5SDimitry Andric 5730b57cec5SDimitry Andric return false; 5740b57cec5SDimitry Andric } 5750b57cec5SDimitry Andric 5760b57cec5SDimitry Andric std::string clang::ento::getVariableName(const FieldDecl *Field) { 5770b57cec5SDimitry Andric // If Field is a captured lambda variable, Field->getName() will return with 5780b57cec5SDimitry Andric // an empty string. We can however acquire it's name from the lambda's 5790b57cec5SDimitry Andric // captures. 5800b57cec5SDimitry Andric const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent()); 5810b57cec5SDimitry Andric 5820b57cec5SDimitry Andric if (CXXParent && CXXParent->isLambda()) { 5830b57cec5SDimitry Andric assert(CXXParent->captures_begin()); 5840b57cec5SDimitry Andric auto It = CXXParent->captures_begin() + Field->getFieldIndex(); 5850b57cec5SDimitry Andric 5860b57cec5SDimitry Andric if (It->capturesVariable()) 5870b57cec5SDimitry Andric return llvm::Twine("/*captured variable*/" + 5880b57cec5SDimitry Andric It->getCapturedVar()->getName()) 5890b57cec5SDimitry Andric .str(); 5900b57cec5SDimitry Andric 5910b57cec5SDimitry Andric if (It->capturesThis()) 5920b57cec5SDimitry Andric return "/*'this' capture*/"; 5930b57cec5SDimitry Andric 5940b57cec5SDimitry Andric llvm_unreachable("No other capture type is expected!"); 5950b57cec5SDimitry Andric } 5960b57cec5SDimitry Andric 5975ffd83dbSDimitry Andric return std::string(Field->getName()); 5980b57cec5SDimitry Andric } 5990b57cec5SDimitry Andric 6000b57cec5SDimitry Andric void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { 6010b57cec5SDimitry Andric auto Chk = Mgr.registerChecker<UninitializedObjectChecker>(); 6020b57cec5SDimitry Andric 6035ffd83dbSDimitry Andric const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions(); 6040b57cec5SDimitry Andric UninitObjCheckerOptions &ChOpts = Chk->Opts; 6050b57cec5SDimitry Andric 6060b57cec5SDimitry Andric ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic"); 6070b57cec5SDimitry Andric ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption( 6080b57cec5SDimitry Andric Chk, "NotesAsWarnings"); 6090b57cec5SDimitry Andric ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption( 6100b57cec5SDimitry Andric Chk, "CheckPointeeInitialization"); 6110b57cec5SDimitry Andric ChOpts.IgnoredRecordsWithFieldPattern = 6125ffd83dbSDimitry Andric std::string(AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField")); 6130b57cec5SDimitry Andric ChOpts.IgnoreGuardedFields = 6140b57cec5SDimitry Andric AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields"); 6150b57cec5SDimitry Andric 6160b57cec5SDimitry Andric std::string ErrorMsg; 6170b57cec5SDimitry Andric if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg)) 6180b57cec5SDimitry Andric Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField", 6190b57cec5SDimitry Andric "a valid regex, building failed with error message " 6200b57cec5SDimitry Andric "\"" + ErrorMsg + "\""); 6210b57cec5SDimitry Andric } 6220b57cec5SDimitry Andric 6235ffd83dbSDimitry Andric bool ento::shouldRegisterUninitializedObjectChecker(const CheckerManager &mgr) { 6240b57cec5SDimitry Andric return true; 6250b57cec5SDimitry Andric } 626