1e5dd7070Spatrick //===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines a checker that reports uninitialized fields in objects
10e5dd7070Spatrick // created after a constructor call.
11e5dd7070Spatrick //
12e5dd7070Spatrick // To read about command line options and how the checker works, refer to the
13e5dd7070Spatrick // top of the file and inline comments in UninitializedObject.h.
14e5dd7070Spatrick //
15e5dd7070Spatrick // Some of the logic is implemented in UninitializedPointee.cpp, to reduce the
16e5dd7070Spatrick // complexity of this file.
17e5dd7070Spatrick //
18e5dd7070Spatrick //===----------------------------------------------------------------------===//
19e5dd7070Spatrick 
20e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21e5dd7070Spatrick #include "UninitializedObject.h"
22e5dd7070Spatrick #include "clang/ASTMatchers/ASTMatchFinder.h"
23e5dd7070Spatrick #include "clang/Driver/DriverDiagnostic.h"
24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
26e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
27e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
28e5dd7070Spatrick 
29e5dd7070Spatrick using namespace clang;
30e5dd7070Spatrick using namespace clang::ento;
31e5dd7070Spatrick using namespace clang::ast_matchers;
32e5dd7070Spatrick 
33e5dd7070Spatrick /// We'll mark fields (and pointee of fields) that are confirmed to be
34e5dd7070Spatrick /// uninitialized as already analyzed.
35e5dd7070Spatrick REGISTER_SET_WITH_PROGRAMSTATE(AnalyzedRegions, const MemRegion *)
36e5dd7070Spatrick 
37e5dd7070Spatrick namespace {
38e5dd7070Spatrick 
39e5dd7070Spatrick class UninitializedObjectChecker
40e5dd7070Spatrick     : public Checker<check::EndFunction, check::DeadSymbols> {
41e5dd7070Spatrick   std::unique_ptr<BuiltinBug> BT_uninitField;
42e5dd7070Spatrick 
43e5dd7070Spatrick public:
44e5dd7070Spatrick   // The fields of this struct will be initialized when registering the checker.
45e5dd7070Spatrick   UninitObjCheckerOptions Opts;
46e5dd7070Spatrick 
UninitializedObjectChecker()47e5dd7070Spatrick   UninitializedObjectChecker()
48e5dd7070Spatrick       : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
49e5dd7070Spatrick 
50e5dd7070Spatrick   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
51e5dd7070Spatrick   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
52e5dd7070Spatrick };
53e5dd7070Spatrick 
54e5dd7070Spatrick /// A basic field type, that is not a pointer or a reference, it's dynamic and
55e5dd7070Spatrick /// static type is the same.
56e5dd7070Spatrick class RegularField final : public FieldNode {
57e5dd7070Spatrick public:
RegularField(const FieldRegion * FR)58e5dd7070Spatrick   RegularField(const FieldRegion *FR) : FieldNode(FR) {}
59e5dd7070Spatrick 
printNoteMsg(llvm::raw_ostream & Out) const60*12c85518Srobert   void printNoteMsg(llvm::raw_ostream &Out) const override {
61e5dd7070Spatrick     Out << "uninitialized field ";
62e5dd7070Spatrick   }
63e5dd7070Spatrick 
printPrefix(llvm::raw_ostream & Out) const64*12c85518Srobert   void printPrefix(llvm::raw_ostream &Out) const override {}
65e5dd7070Spatrick 
printNode(llvm::raw_ostream & Out) const66*12c85518Srobert   void printNode(llvm::raw_ostream &Out) const override {
67e5dd7070Spatrick     Out << getVariableName(getDecl());
68e5dd7070Spatrick   }
69e5dd7070Spatrick 
printSeparator(llvm::raw_ostream & Out) const70*12c85518Srobert   void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; }
71e5dd7070Spatrick };
72e5dd7070Spatrick 
73e5dd7070Spatrick /// Represents that the FieldNode that comes after this is declared in a base
74e5dd7070Spatrick /// of the previous FieldNode. As such, this descendant doesn't wrap a
75e5dd7070Spatrick /// FieldRegion, and is purely a tool to describe a relation between two other
76e5dd7070Spatrick /// FieldRegion wrapping descendants.
77e5dd7070Spatrick class BaseClass final : public FieldNode {
78e5dd7070Spatrick   const QualType BaseClassT;
79e5dd7070Spatrick 
80e5dd7070Spatrick public:
BaseClass(const QualType & T)81e5dd7070Spatrick   BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) {
82e5dd7070Spatrick     assert(!T.isNull());
83e5dd7070Spatrick     assert(T->getAsCXXRecordDecl());
84e5dd7070Spatrick   }
85e5dd7070Spatrick 
printNoteMsg(llvm::raw_ostream & Out) const86*12c85518Srobert   void printNoteMsg(llvm::raw_ostream &Out) const override {
87e5dd7070Spatrick     llvm_unreachable("This node can never be the final node in the "
88e5dd7070Spatrick                      "fieldchain!");
89e5dd7070Spatrick   }
90e5dd7070Spatrick 
printPrefix(llvm::raw_ostream & Out) const91*12c85518Srobert   void printPrefix(llvm::raw_ostream &Out) const override {}
92e5dd7070Spatrick 
printNode(llvm::raw_ostream & Out) const93*12c85518Srobert   void printNode(llvm::raw_ostream &Out) const override {
94e5dd7070Spatrick     Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";
95e5dd7070Spatrick   }
96e5dd7070Spatrick 
printSeparator(llvm::raw_ostream & Out) const97*12c85518Srobert   void printSeparator(llvm::raw_ostream &Out) const override {}
98e5dd7070Spatrick 
isBase() const99*12c85518Srobert   bool isBase() const override { return true; }
100e5dd7070Spatrick };
101e5dd7070Spatrick 
102e5dd7070Spatrick } // end of anonymous namespace
103e5dd7070Spatrick 
104e5dd7070Spatrick // Utility function declarations.
105e5dd7070Spatrick 
106e5dd7070Spatrick /// Returns the region that was constructed by CtorDecl, or nullptr if that
107e5dd7070Spatrick /// isn't possible.
108e5dd7070Spatrick static const TypedValueRegion *
109e5dd7070Spatrick getConstructedRegion(const CXXConstructorDecl *CtorDecl,
110e5dd7070Spatrick                      CheckerContext &Context);
111e5dd7070Spatrick 
112e5dd7070Spatrick /// Checks whether the object constructed by \p Ctor will be analyzed later
113e5dd7070Spatrick /// (e.g. if the object is a field of another object, in which case we'd check
114e5dd7070Spatrick /// it multiple times).
115e5dd7070Spatrick static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
116e5dd7070Spatrick                                       CheckerContext &Context);
117e5dd7070Spatrick 
118e5dd7070Spatrick /// Checks whether RD contains a field with a name or type name that matches
119e5dd7070Spatrick /// \p Pattern.
120e5dd7070Spatrick static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
121e5dd7070Spatrick 
122e5dd7070Spatrick /// Checks _syntactically_ whether it is possible to access FD from the record
123e5dd7070Spatrick /// that contains it without a preceding assert (even if that access happens
124e5dd7070Spatrick /// inside a method). This is mainly used for records that act like unions, like
125e5dd7070Spatrick /// having multiple bit fields, with only a fraction being properly initialized.
126e5dd7070Spatrick /// If these fields are properly guarded with asserts, this method returns
127e5dd7070Spatrick /// false.
128e5dd7070Spatrick ///
129e5dd7070Spatrick /// Since this check is done syntactically, this method could be inaccurate.
130e5dd7070Spatrick static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State);
131e5dd7070Spatrick 
132e5dd7070Spatrick //===----------------------------------------------------------------------===//
133e5dd7070Spatrick //                  Methods for UninitializedObjectChecker.
134e5dd7070Spatrick //===----------------------------------------------------------------------===//
135e5dd7070Spatrick 
checkEndFunction(const ReturnStmt * RS,CheckerContext & Context) const136e5dd7070Spatrick void UninitializedObjectChecker::checkEndFunction(
137e5dd7070Spatrick     const ReturnStmt *RS, CheckerContext &Context) const {
138e5dd7070Spatrick 
139e5dd7070Spatrick   const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
140e5dd7070Spatrick       Context.getLocationContext()->getDecl());
141e5dd7070Spatrick   if (!CtorDecl)
142e5dd7070Spatrick     return;
143e5dd7070Spatrick 
144e5dd7070Spatrick   if (!CtorDecl->isUserProvided())
145e5dd7070Spatrick     return;
146e5dd7070Spatrick 
147e5dd7070Spatrick   if (CtorDecl->getParent()->isUnion())
148e5dd7070Spatrick     return;
149e5dd7070Spatrick 
150e5dd7070Spatrick   // This avoids essentially the same error being reported multiple times.
151e5dd7070Spatrick   if (willObjectBeAnalyzedLater(CtorDecl, Context))
152e5dd7070Spatrick     return;
153e5dd7070Spatrick 
154e5dd7070Spatrick   const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context);
155e5dd7070Spatrick   if (!R)
156e5dd7070Spatrick     return;
157e5dd7070Spatrick 
158e5dd7070Spatrick   FindUninitializedFields F(Context.getState(), R, Opts);
159e5dd7070Spatrick 
160e5dd7070Spatrick   std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo =
161e5dd7070Spatrick       F.getResults();
162e5dd7070Spatrick 
163e5dd7070Spatrick   ProgramStateRef UpdatedState = UninitInfo.first;
164e5dd7070Spatrick   const UninitFieldMap &UninitFields = UninitInfo.second;
165e5dd7070Spatrick 
166e5dd7070Spatrick   if (UninitFields.empty()) {
167e5dd7070Spatrick     Context.addTransition(UpdatedState);
168e5dd7070Spatrick     return;
169e5dd7070Spatrick   }
170e5dd7070Spatrick 
171e5dd7070Spatrick   // There are uninitialized fields in the record.
172e5dd7070Spatrick 
173e5dd7070Spatrick   ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState);
174e5dd7070Spatrick   if (!Node)
175e5dd7070Spatrick     return;
176e5dd7070Spatrick 
177e5dd7070Spatrick   PathDiagnosticLocation LocUsedForUniqueing;
178e5dd7070Spatrick   const Stmt *CallSite = Context.getStackFrame()->getCallSite();
179e5dd7070Spatrick   if (CallSite)
180e5dd7070Spatrick     LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
181e5dd7070Spatrick         CallSite, Context.getSourceManager(), Node->getLocationContext());
182e5dd7070Spatrick 
183e5dd7070Spatrick   // For Plist consumers that don't support notes just yet, we'll convert notes
184e5dd7070Spatrick   // to warnings.
185e5dd7070Spatrick   if (Opts.ShouldConvertNotesToWarnings) {
186e5dd7070Spatrick     for (const auto &Pair : UninitFields) {
187e5dd7070Spatrick 
188e5dd7070Spatrick       auto Report = std::make_unique<PathSensitiveBugReport>(
189e5dd7070Spatrick           *BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
190e5dd7070Spatrick           Node->getLocationContext()->getDecl());
191e5dd7070Spatrick       Context.emitReport(std::move(Report));
192e5dd7070Spatrick     }
193e5dd7070Spatrick     return;
194e5dd7070Spatrick   }
195e5dd7070Spatrick 
196e5dd7070Spatrick   SmallString<100> WarningBuf;
197e5dd7070Spatrick   llvm::raw_svector_ostream WarningOS(WarningBuf);
198e5dd7070Spatrick   WarningOS << UninitFields.size() << " uninitialized field"
199e5dd7070Spatrick             << (UninitFields.size() == 1 ? "" : "s")
200e5dd7070Spatrick             << " at the end of the constructor call";
201e5dd7070Spatrick 
202e5dd7070Spatrick   auto Report = std::make_unique<PathSensitiveBugReport>(
203e5dd7070Spatrick       *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
204e5dd7070Spatrick       Node->getLocationContext()->getDecl());
205e5dd7070Spatrick 
206e5dd7070Spatrick   for (const auto &Pair : UninitFields) {
207e5dd7070Spatrick     Report->addNote(Pair.second,
208e5dd7070Spatrick                     PathDiagnosticLocation::create(Pair.first->getDecl(),
209e5dd7070Spatrick                                                    Context.getSourceManager()));
210e5dd7070Spatrick   }
211e5dd7070Spatrick   Context.emitReport(std::move(Report));
212e5dd7070Spatrick }
213e5dd7070Spatrick 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const214e5dd7070Spatrick void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR,
215e5dd7070Spatrick                                                   CheckerContext &C) const {
216e5dd7070Spatrick   ProgramStateRef State = C.getState();
217e5dd7070Spatrick   for (const MemRegion *R : State->get<AnalyzedRegions>()) {
218e5dd7070Spatrick     if (!SR.isLiveRegion(R))
219e5dd7070Spatrick       State = State->remove<AnalyzedRegions>(R);
220e5dd7070Spatrick   }
221e5dd7070Spatrick }
222e5dd7070Spatrick 
223e5dd7070Spatrick //===----------------------------------------------------------------------===//
224e5dd7070Spatrick //                   Methods for FindUninitializedFields.
225e5dd7070Spatrick //===----------------------------------------------------------------------===//
226e5dd7070Spatrick 
FindUninitializedFields(ProgramStateRef State,const TypedValueRegion * const R,const UninitObjCheckerOptions & Opts)227e5dd7070Spatrick FindUninitializedFields::FindUninitializedFields(
228e5dd7070Spatrick     ProgramStateRef State, const TypedValueRegion *const R,
229e5dd7070Spatrick     const UninitObjCheckerOptions &Opts)
230e5dd7070Spatrick     : State(State), ObjectR(R), Opts(Opts) {
231e5dd7070Spatrick 
232e5dd7070Spatrick   isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory));
233e5dd7070Spatrick 
234e5dd7070Spatrick   // In non-pedantic mode, if ObjectR doesn't contain a single initialized
235e5dd7070Spatrick   // field, we'll assume that Object was intentionally left uninitialized.
236e5dd7070Spatrick   if (!Opts.IsPedantic && !isAnyFieldInitialized())
237e5dd7070Spatrick     UninitFields.clear();
238e5dd7070Spatrick }
239e5dd7070Spatrick 
addFieldToUninits(FieldChainInfo Chain,const MemRegion * PointeeR)240e5dd7070Spatrick bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
241e5dd7070Spatrick                                                 const MemRegion *PointeeR) {
242e5dd7070Spatrick   const FieldRegion *FR = Chain.getUninitRegion();
243e5dd7070Spatrick 
244e5dd7070Spatrick   assert((PointeeR || !isDereferencableType(FR->getDecl()->getType())) &&
245e5dd7070Spatrick          "One must also pass the pointee region as a parameter for "
246e5dd7070Spatrick          "dereferenceable fields!");
247e5dd7070Spatrick 
248e5dd7070Spatrick   if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
249e5dd7070Spatrick           FR->getDecl()->getLocation()))
250e5dd7070Spatrick     return false;
251e5dd7070Spatrick 
252e5dd7070Spatrick   if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State))
253e5dd7070Spatrick     return false;
254e5dd7070Spatrick 
255e5dd7070Spatrick   if (State->contains<AnalyzedRegions>(FR))
256e5dd7070Spatrick     return false;
257e5dd7070Spatrick 
258e5dd7070Spatrick   if (PointeeR) {
259e5dd7070Spatrick     if (State->contains<AnalyzedRegions>(PointeeR)) {
260e5dd7070Spatrick       return false;
261e5dd7070Spatrick     }
262e5dd7070Spatrick     State = State->add<AnalyzedRegions>(PointeeR);
263e5dd7070Spatrick   }
264e5dd7070Spatrick 
265e5dd7070Spatrick   State = State->add<AnalyzedRegions>(FR);
266e5dd7070Spatrick 
267e5dd7070Spatrick   UninitFieldMap::mapped_type NoteMsgBuf;
268e5dd7070Spatrick   llvm::raw_svector_ostream OS(NoteMsgBuf);
269e5dd7070Spatrick   Chain.printNoteMsg(OS);
270e5dd7070Spatrick 
271e5dd7070Spatrick   return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second;
272e5dd7070Spatrick }
273e5dd7070Spatrick 
isNonUnionUninit(const TypedValueRegion * R,FieldChainInfo LocalChain)274e5dd7070Spatrick bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
275e5dd7070Spatrick                                                FieldChainInfo LocalChain) {
276e5dd7070Spatrick   assert(R->getValueType()->isRecordType() &&
277e5dd7070Spatrick          !R->getValueType()->isUnionType() &&
278e5dd7070Spatrick          "This method only checks non-union record objects!");
279e5dd7070Spatrick 
280e5dd7070Spatrick   const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition();
281e5dd7070Spatrick 
282e5dd7070Spatrick   if (!RD) {
283e5dd7070Spatrick     IsAnyFieldInitialized = true;
284e5dd7070Spatrick     return true;
285e5dd7070Spatrick   }
286e5dd7070Spatrick 
287e5dd7070Spatrick   if (!Opts.IgnoredRecordsWithFieldPattern.empty() &&
288e5dd7070Spatrick       shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) {
289e5dd7070Spatrick     IsAnyFieldInitialized = true;
290e5dd7070Spatrick     return false;
291e5dd7070Spatrick   }
292e5dd7070Spatrick 
293e5dd7070Spatrick   bool ContainsUninitField = false;
294e5dd7070Spatrick 
295e5dd7070Spatrick   // Are all of this non-union's fields initialized?
296e5dd7070Spatrick   for (const FieldDecl *I : RD->fields()) {
297e5dd7070Spatrick 
298e5dd7070Spatrick     const auto FieldVal =
299e5dd7070Spatrick         State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
300e5dd7070Spatrick     const auto *FR = FieldVal.getRegionAs<FieldRegion>();
301e5dd7070Spatrick     QualType T = I->getType();
302e5dd7070Spatrick 
303e5dd7070Spatrick     // If LocalChain already contains FR, then we encountered a cyclic
304e5dd7070Spatrick     // reference. In this case, region FR is already under checking at an
305e5dd7070Spatrick     // earlier node in the directed tree.
306e5dd7070Spatrick     if (LocalChain.contains(FR))
307e5dd7070Spatrick       return false;
308e5dd7070Spatrick 
309e5dd7070Spatrick     if (T->isStructureOrClassType()) {
310e5dd7070Spatrick       if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR))))
311e5dd7070Spatrick         ContainsUninitField = true;
312e5dd7070Spatrick       continue;
313e5dd7070Spatrick     }
314e5dd7070Spatrick 
315e5dd7070Spatrick     if (T->isUnionType()) {
316e5dd7070Spatrick       if (isUnionUninit(FR)) {
317e5dd7070Spatrick         if (addFieldToUninits(LocalChain.add(RegularField(FR))))
318e5dd7070Spatrick           ContainsUninitField = true;
319e5dd7070Spatrick       } else
320e5dd7070Spatrick         IsAnyFieldInitialized = true;
321e5dd7070Spatrick       continue;
322e5dd7070Spatrick     }
323e5dd7070Spatrick 
324e5dd7070Spatrick     if (T->isArrayType()) {
325e5dd7070Spatrick       IsAnyFieldInitialized = true;
326e5dd7070Spatrick       continue;
327e5dd7070Spatrick     }
328e5dd7070Spatrick 
329e5dd7070Spatrick     SVal V = State->getSVal(FieldVal);
330e5dd7070Spatrick 
331*12c85518Srobert     if (isDereferencableType(T) || isa<nonloc::LocAsInteger>(V)) {
332e5dd7070Spatrick       if (isDereferencableUninit(FR, LocalChain))
333e5dd7070Spatrick         ContainsUninitField = true;
334e5dd7070Spatrick       continue;
335e5dd7070Spatrick     }
336e5dd7070Spatrick 
337e5dd7070Spatrick     if (isPrimitiveType(T)) {
338e5dd7070Spatrick       if (isPrimitiveUninit(V)) {
339e5dd7070Spatrick         if (addFieldToUninits(LocalChain.add(RegularField(FR))))
340e5dd7070Spatrick           ContainsUninitField = true;
341e5dd7070Spatrick       }
342e5dd7070Spatrick       continue;
343e5dd7070Spatrick     }
344e5dd7070Spatrick 
345e5dd7070Spatrick     llvm_unreachable("All cases are handled!");
346e5dd7070Spatrick   }
347e5dd7070Spatrick 
348e5dd7070Spatrick   // Checking bases. The checker will regard inherited data members as direct
349e5dd7070Spatrick   // fields.
350e5dd7070Spatrick   const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
351e5dd7070Spatrick   if (!CXXRD)
352e5dd7070Spatrick     return ContainsUninitField;
353e5dd7070Spatrick 
354e5dd7070Spatrick   for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
355e5dd7070Spatrick     const auto *BaseRegion = State->getLValue(BaseSpec, R)
356e5dd7070Spatrick                                  .castAs<loc::MemRegionVal>()
357e5dd7070Spatrick                                  .getRegionAs<TypedValueRegion>();
358e5dd7070Spatrick 
359e5dd7070Spatrick     // If the head of the list is also a BaseClass, we'll overwrite it to avoid
360e5dd7070Spatrick     // note messages like 'this->A::B::x'.
361e5dd7070Spatrick     if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()) {
362e5dd7070Spatrick       if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead(
363e5dd7070Spatrick                                            BaseClass(BaseSpec.getType()))))
364e5dd7070Spatrick         ContainsUninitField = true;
365e5dd7070Spatrick     } else {
366e5dd7070Spatrick       if (isNonUnionUninit(BaseRegion,
367e5dd7070Spatrick                            LocalChain.add(BaseClass(BaseSpec.getType()))))
368e5dd7070Spatrick         ContainsUninitField = true;
369e5dd7070Spatrick     }
370e5dd7070Spatrick   }
371e5dd7070Spatrick 
372e5dd7070Spatrick   return ContainsUninitField;
373e5dd7070Spatrick }
374e5dd7070Spatrick 
isUnionUninit(const TypedValueRegion * R)375e5dd7070Spatrick bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
376e5dd7070Spatrick   assert(R->getValueType()->isUnionType() &&
377e5dd7070Spatrick          "This method only checks union objects!");
378e5dd7070Spatrick   // TODO: Implement support for union fields.
379e5dd7070Spatrick   return false;
380e5dd7070Spatrick }
381e5dd7070Spatrick 
isPrimitiveUninit(const SVal & V)382e5dd7070Spatrick bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
383e5dd7070Spatrick   if (V.isUndef())
384e5dd7070Spatrick     return true;
385e5dd7070Spatrick 
386e5dd7070Spatrick   IsAnyFieldInitialized = true;
387e5dd7070Spatrick   return false;
388e5dd7070Spatrick }
389e5dd7070Spatrick 
390e5dd7070Spatrick //===----------------------------------------------------------------------===//
391e5dd7070Spatrick //                       Methods for FieldChainInfo.
392e5dd7070Spatrick //===----------------------------------------------------------------------===//
393e5dd7070Spatrick 
contains(const FieldRegion * FR) const394e5dd7070Spatrick bool FieldChainInfo::contains(const FieldRegion *FR) const {
395e5dd7070Spatrick   for (const FieldNode &Node : Chain) {
396e5dd7070Spatrick     if (Node.isSameRegion(FR))
397e5dd7070Spatrick       return true;
398e5dd7070Spatrick   }
399e5dd7070Spatrick   return false;
400e5dd7070Spatrick }
401e5dd7070Spatrick 
402e5dd7070Spatrick /// Prints every element except the last to `Out`. Since ImmutableLists store
403e5dd7070Spatrick /// elements in reverse order, and have no reverse iterators, we use a
404e5dd7070Spatrick /// recursive function to print the fieldchain correctly. The last element in
405e5dd7070Spatrick /// the chain is to be printed by `FieldChainInfo::print`.
406e5dd7070Spatrick static void printTail(llvm::raw_ostream &Out,
407e5dd7070Spatrick                       const FieldChainInfo::FieldChain L);
408e5dd7070Spatrick 
409e5dd7070Spatrick // FIXME: This function constructs an incorrect string in the following case:
410e5dd7070Spatrick //
411e5dd7070Spatrick //   struct Base { int x; };
412e5dd7070Spatrick //   struct D1 : Base {}; struct D2 : Base {};
413e5dd7070Spatrick //
414e5dd7070Spatrick //   struct MostDerived : D1, D2 {
415e5dd7070Spatrick //     MostDerived() {}
416e5dd7070Spatrick //   }
417e5dd7070Spatrick //
418e5dd7070Spatrick // A call to MostDerived::MostDerived() will cause two notes that say
419e5dd7070Spatrick // "uninitialized field 'this->x'", but we can't refer to 'x' directly,
420e5dd7070Spatrick // we need an explicit namespace resolution whether the uninit field was
421e5dd7070Spatrick // 'D1::x' or 'D2::x'.
printNoteMsg(llvm::raw_ostream & Out) const422e5dd7070Spatrick void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const {
423e5dd7070Spatrick   if (Chain.isEmpty())
424e5dd7070Spatrick     return;
425e5dd7070Spatrick 
426e5dd7070Spatrick   const FieldNode &LastField = getHead();
427e5dd7070Spatrick 
428e5dd7070Spatrick   LastField.printNoteMsg(Out);
429e5dd7070Spatrick   Out << '\'';
430e5dd7070Spatrick 
431e5dd7070Spatrick   for (const FieldNode &Node : Chain)
432e5dd7070Spatrick     Node.printPrefix(Out);
433e5dd7070Spatrick 
434e5dd7070Spatrick   Out << "this->";
435e5dd7070Spatrick   printTail(Out, Chain.getTail());
436e5dd7070Spatrick   LastField.printNode(Out);
437e5dd7070Spatrick   Out << '\'';
438e5dd7070Spatrick }
439e5dd7070Spatrick 
printTail(llvm::raw_ostream & Out,const FieldChainInfo::FieldChain L)440e5dd7070Spatrick static void printTail(llvm::raw_ostream &Out,
441e5dd7070Spatrick                       const FieldChainInfo::FieldChain L) {
442e5dd7070Spatrick   if (L.isEmpty())
443e5dd7070Spatrick     return;
444e5dd7070Spatrick 
445e5dd7070Spatrick   printTail(Out, L.getTail());
446e5dd7070Spatrick 
447e5dd7070Spatrick   L.getHead().printNode(Out);
448e5dd7070Spatrick   L.getHead().printSeparator(Out);
449e5dd7070Spatrick }
450e5dd7070Spatrick 
451e5dd7070Spatrick //===----------------------------------------------------------------------===//
452e5dd7070Spatrick //                           Utility functions.
453e5dd7070Spatrick //===----------------------------------------------------------------------===//
454e5dd7070Spatrick 
455e5dd7070Spatrick static const TypedValueRegion *
getConstructedRegion(const CXXConstructorDecl * CtorDecl,CheckerContext & Context)456e5dd7070Spatrick getConstructedRegion(const CXXConstructorDecl *CtorDecl,
457e5dd7070Spatrick                      CheckerContext &Context) {
458e5dd7070Spatrick 
459e5dd7070Spatrick   Loc ThisLoc =
460e5dd7070Spatrick       Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame());
461e5dd7070Spatrick 
462e5dd7070Spatrick   SVal ObjectV = Context.getState()->getSVal(ThisLoc);
463e5dd7070Spatrick 
464e5dd7070Spatrick   auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>();
465e5dd7070Spatrick   if (R && !R->getValueType()->getAsCXXRecordDecl())
466e5dd7070Spatrick     return nullptr;
467e5dd7070Spatrick 
468e5dd7070Spatrick   return R;
469e5dd7070Spatrick }
470e5dd7070Spatrick 
willObjectBeAnalyzedLater(const CXXConstructorDecl * Ctor,CheckerContext & Context)471e5dd7070Spatrick static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
472e5dd7070Spatrick                                       CheckerContext &Context) {
473e5dd7070Spatrick 
474e5dd7070Spatrick   const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context);
475e5dd7070Spatrick   if (!CurrRegion)
476e5dd7070Spatrick     return false;
477e5dd7070Spatrick 
478e5dd7070Spatrick   const LocationContext *LC = Context.getLocationContext();
479e5dd7070Spatrick   while ((LC = LC->getParent())) {
480e5dd7070Spatrick 
481e5dd7070Spatrick     // If \p Ctor was called by another constructor.
482e5dd7070Spatrick     const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
483e5dd7070Spatrick     if (!OtherCtor)
484e5dd7070Spatrick       continue;
485e5dd7070Spatrick 
486e5dd7070Spatrick     const TypedValueRegion *OtherRegion =
487e5dd7070Spatrick         getConstructedRegion(OtherCtor, Context);
488e5dd7070Spatrick     if (!OtherRegion)
489e5dd7070Spatrick       continue;
490e5dd7070Spatrick 
491e5dd7070Spatrick     // If the CurrRegion is a subregion of OtherRegion, it will be analyzed
492e5dd7070Spatrick     // during the analysis of OtherRegion.
493e5dd7070Spatrick     if (CurrRegion->isSubRegionOf(OtherRegion))
494e5dd7070Spatrick       return true;
495e5dd7070Spatrick   }
496e5dd7070Spatrick 
497e5dd7070Spatrick   return false;
498e5dd7070Spatrick }
499e5dd7070Spatrick 
shouldIgnoreRecord(const RecordDecl * RD,StringRef Pattern)500e5dd7070Spatrick static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
501e5dd7070Spatrick   llvm::Regex R(Pattern);
502e5dd7070Spatrick 
503e5dd7070Spatrick   for (const FieldDecl *FD : RD->fields()) {
504e5dd7070Spatrick     if (R.match(FD->getType().getAsString()))
505e5dd7070Spatrick       return true;
506e5dd7070Spatrick     if (R.match(FD->getName()))
507e5dd7070Spatrick       return true;
508e5dd7070Spatrick   }
509e5dd7070Spatrick 
510e5dd7070Spatrick   return false;
511e5dd7070Spatrick }
512e5dd7070Spatrick 
getMethodBody(const CXXMethodDecl * M)513e5dd7070Spatrick static const Stmt *getMethodBody(const CXXMethodDecl *M) {
514e5dd7070Spatrick   if (isa<CXXConstructorDecl>(M))
515e5dd7070Spatrick     return nullptr;
516e5dd7070Spatrick 
517e5dd7070Spatrick   if (!M->isDefined())
518e5dd7070Spatrick     return nullptr;
519e5dd7070Spatrick 
520e5dd7070Spatrick   return M->getDefinition()->getBody();
521e5dd7070Spatrick }
522e5dd7070Spatrick 
hasUnguardedAccess(const FieldDecl * FD,ProgramStateRef State)523e5dd7070Spatrick static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) {
524e5dd7070Spatrick 
525e5dd7070Spatrick   if (FD->getAccess() == AccessSpecifier::AS_public)
526e5dd7070Spatrick     return true;
527e5dd7070Spatrick 
528e5dd7070Spatrick   const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent());
529e5dd7070Spatrick 
530e5dd7070Spatrick   if (!Parent)
531e5dd7070Spatrick     return true;
532e5dd7070Spatrick 
533e5dd7070Spatrick   Parent = Parent->getDefinition();
534e5dd7070Spatrick   assert(Parent && "The record's definition must be avaible if an uninitialized"
535e5dd7070Spatrick                    " field of it was found!");
536e5dd7070Spatrick 
537e5dd7070Spatrick   ASTContext &AC = State->getStateManager().getContext();
538e5dd7070Spatrick 
539e5dd7070Spatrick   auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access");
540e5dd7070Spatrick 
541e5dd7070Spatrick   auto AssertLikeM = callExpr(callee(functionDecl(
542ec727ea7Spatrick       hasAnyName("exit", "panic", "error", "Assert", "assert", "ziperr",
543ec727ea7Spatrick                  "assfail", "db_error", "__assert", "__assert2", "_wassert",
544ec727ea7Spatrick                  "__assert_rtn", "__assert_fail", "dtrace_assfail",
545ec727ea7Spatrick                  "yy_fatal_error", "_XCAssertionFailureHandler",
546ec727ea7Spatrick                  "_DTAssertionFailureHandler", "_TSAssertionFailureHandler"))));
547e5dd7070Spatrick 
548e5dd7070Spatrick   auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn())));
549e5dd7070Spatrick 
550e5dd7070Spatrick   auto GuardM =
551e5dd7070Spatrick       stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM,
552e5dd7070Spatrick             NoReturnFuncM))
553e5dd7070Spatrick           .bind("guard");
554e5dd7070Spatrick 
555e5dd7070Spatrick   for (const CXXMethodDecl *M : Parent->methods()) {
556e5dd7070Spatrick     const Stmt *MethodBody = getMethodBody(M);
557e5dd7070Spatrick     if (!MethodBody)
558e5dd7070Spatrick       continue;
559e5dd7070Spatrick 
560e5dd7070Spatrick     auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC);
561e5dd7070Spatrick     if (Accesses.empty())
562e5dd7070Spatrick       continue;
563e5dd7070Spatrick     const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access");
564e5dd7070Spatrick     assert(FirstAccess);
565e5dd7070Spatrick 
566e5dd7070Spatrick     auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC);
567e5dd7070Spatrick     if (Guards.empty())
568e5dd7070Spatrick       return true;
569e5dd7070Spatrick     const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard");
570e5dd7070Spatrick     assert(FirstGuard);
571e5dd7070Spatrick 
572e5dd7070Spatrick     if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc())
573e5dd7070Spatrick       return true;
574e5dd7070Spatrick   }
575e5dd7070Spatrick 
576e5dd7070Spatrick   return false;
577e5dd7070Spatrick }
578e5dd7070Spatrick 
getVariableName(const FieldDecl * Field)579e5dd7070Spatrick std::string clang::ento::getVariableName(const FieldDecl *Field) {
580e5dd7070Spatrick   // If Field is a captured lambda variable, Field->getName() will return with
581e5dd7070Spatrick   // an empty string. We can however acquire it's name from the lambda's
582e5dd7070Spatrick   // captures.
583e5dd7070Spatrick   const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
584e5dd7070Spatrick 
585e5dd7070Spatrick   if (CXXParent && CXXParent->isLambda()) {
586e5dd7070Spatrick     assert(CXXParent->captures_begin());
587e5dd7070Spatrick     auto It = CXXParent->captures_begin() + Field->getFieldIndex();
588e5dd7070Spatrick 
589e5dd7070Spatrick     if (It->capturesVariable())
590e5dd7070Spatrick       return llvm::Twine("/*captured variable*/" +
591e5dd7070Spatrick                          It->getCapturedVar()->getName())
592e5dd7070Spatrick           .str();
593e5dd7070Spatrick 
594e5dd7070Spatrick     if (It->capturesThis())
595e5dd7070Spatrick       return "/*'this' capture*/";
596e5dd7070Spatrick 
597e5dd7070Spatrick     llvm_unreachable("No other capture type is expected!");
598e5dd7070Spatrick   }
599e5dd7070Spatrick 
600ec727ea7Spatrick   return std::string(Field->getName());
601e5dd7070Spatrick }
602e5dd7070Spatrick 
registerUninitializedObjectChecker(CheckerManager & Mgr)603e5dd7070Spatrick void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
604e5dd7070Spatrick   auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
605e5dd7070Spatrick 
606ec727ea7Spatrick   const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
607e5dd7070Spatrick   UninitObjCheckerOptions &ChOpts = Chk->Opts;
608e5dd7070Spatrick 
609e5dd7070Spatrick   ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic");
610e5dd7070Spatrick   ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption(
611e5dd7070Spatrick       Chk, "NotesAsWarnings");
612e5dd7070Spatrick   ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
613e5dd7070Spatrick       Chk, "CheckPointeeInitialization");
614e5dd7070Spatrick   ChOpts.IgnoredRecordsWithFieldPattern =
615ec727ea7Spatrick       std::string(AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField"));
616e5dd7070Spatrick   ChOpts.IgnoreGuardedFields =
617e5dd7070Spatrick       AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields");
618e5dd7070Spatrick 
619e5dd7070Spatrick   std::string ErrorMsg;
620e5dd7070Spatrick   if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg))
621e5dd7070Spatrick     Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField",
622e5dd7070Spatrick         "a valid regex, building failed with error message "
623e5dd7070Spatrick         "\"" + ErrorMsg + "\"");
624e5dd7070Spatrick }
625e5dd7070Spatrick 
shouldRegisterUninitializedObjectChecker(const CheckerManager & mgr)626ec727ea7Spatrick bool ento::shouldRegisterUninitializedObjectChecker(const CheckerManager &mgr) {
627e5dd7070Spatrick   return true;
628e5dd7070Spatrick }
629