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