blob: 63e4b817da33565bc69732d1891bcece38a1d903 [file] [log] [blame]
Kristof Umann30f08652018-06-18 11:50:17 +00001//===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines a checker that reports uninitialized fields in objects
11// created after a constructor call.
12//
13// This checker has an option "Pedantic" (boolean). If its not set or is set to
14// false, the checker won't emit warnings for objects that don't have at least
15// one initialized field. This may be set with
16// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`.
17//
18//===----------------------------------------------------------------------===//
19
20#include "ClangSACheckers.h"
21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22#include "clang/StaticAnalyzer/Core/Checker.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24#include <algorithm>
25
26using namespace clang;
27using namespace clang::ento;
28
29namespace {
30
31class UninitializedObjectChecker : public Checker<check::EndFunction> {
32 std::unique_ptr<BuiltinBug> BT_uninitField;
33
34public:
35 bool IsPedantic; // Will be initialized when registering the checker.
36
37 UninitializedObjectChecker()
38 : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
39 void checkEndFunction(CheckerContext &C) const;
40};
41
42llvm::ImmutableListFactory<const FieldRegion *> Factory;
43
44/// Represents a field chain. A field chain is a vector of fields where the
45/// first element of the chain is the object under checking (not stored), and
46/// every other element is a field, and the element that precedes it is the
47/// object that contains it.
48///
49/// Note that this class is immutable, and new fields may only be added through
50/// constructor calls.
51class FieldChainInfo {
52 using FieldChain = llvm::ImmutableList<const FieldRegion *>;
53
54 FieldChain Chain;
55
56 const bool IsDereferenced = false;
57
58public:
59 FieldChainInfo() = default;
60
61 FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced)
62 : Chain(Other.Chain), IsDereferenced(IsDereferenced) {}
63
64 FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR,
65 const bool IsDereferenced = false);
66
67 bool contains(const FieldRegion *FR) const { return Chain.contains(FR); }
68 bool isPointer() const;
69
70 /// If this is a fieldchain whose last element is an uninitialized region of a
71 /// pointer type, `IsDereferenced` will store whether the pointer itself or
72 /// the pointee is uninitialized.
73 bool isDereferenced() const;
74 const FieldDecl *getEndOfChain() const;
75 void print(llvm::raw_ostream &Out) const;
76
77private:
78 /// Prints every element except the last to `Out`. Since ImmutableLists store
79 /// elements in reverse order, and have no reverse iterators, we use a
80 /// recursive function to print the fieldchain correctly. The last element in
81 /// the chain is to be printed by `print`.
82 static void printTail(llvm::raw_ostream &Out,
83 const llvm::ImmutableListImpl<const FieldRegion *> *L);
84 friend struct FieldChainInfoComparator;
85};
86
87struct FieldChainInfoComparator {
88 bool operator()(const FieldChainInfo &lhs, const FieldChainInfo &rhs) {
89 assert(!lhs.Chain.isEmpty() && !rhs.Chain.isEmpty() &&
90 "Attempted to store an empty fieldchain!");
91 return *lhs.Chain.begin() < *rhs.Chain.begin();
92 }
93};
94
95using UninitFieldSet = std::set<FieldChainInfo, FieldChainInfoComparator>;
96
97/// Searches for and stores uninitialized fields in a non-union object.
98class FindUninitializedFields {
99 ProgramStateRef State;
100 const TypedValueRegion *const ObjectR;
101
102 const bool IsPedantic;
103 bool IsAnyFieldInitialized = false;
104
105 UninitFieldSet UninitFields;
106
107public:
108 FindUninitializedFields(ProgramStateRef State,
109 const TypedValueRegion *const R, bool IsPedantic);
110 const UninitFieldSet &getUninitFields();
111
112private:
113 /// Adds a FieldChainInfo object to UninitFields. Return true if an insertion
114 /// took place.
115 bool addFieldToUninits(FieldChainInfo LocalChain);
116
117 // For the purposes of this checker, we'll regard the object under checking as
118 // a directed tree, where
119 // * the root is the object under checking
120 // * every node is an object that is
121 // - a union
122 // - a non-union record
123 // - a pointer/reference
124 // - an array
125 // - of a member pointer type
126 // - of a primitive type, which we'll define as either a BuiltinType or
127 // EnumeralType.
128 // * the parent of each node is the object that contains it
129 // * every leaf is an array, a primitive object, a member pointer, a nullptr
130 // or an undefined pointer.
131 //
132 // Example:
133 //
134 // struct A {
135 // struct B {
136 // int x, y = 0;
137 // };
138 // B b;
139 // int *iptr = new int;
140 // B* bptr;
141 //
142 // A() {}
143 // };
144 //
145 // The directed tree:
146 //
147 // ->x
148 // /
149 // ->b--->y
150 // /
151 // A-->iptr->(int value)
152 // \
153 // ->bptr
154 //
155 // From this we'll construct a vector of fieldchains, where each fieldchain
156 // represents an uninitialized field. An uninitialized field may be a
157 // primitive object, a member pointer, a pointer, a pointee or a union without
158 // a single initialized field.
159 // In the above example, for the default constructor call we'll end up with
160 // these fieldchains:
161 //
162 // this->b.x
163 // this->iptr (pointee uninit)
164 // this->bptr (pointer uninit)
165 //
166 // We'll traverse each node of the above graph with the appropiate one of
167 // these methods:
168
169 /// This method checks a region of a union object, and returns true if no
170 /// field is initialized within the region.
171 bool isUnionUninit(const TypedValueRegion *R);
172
173 /// This method checks a region of a non-union object, and returns true if
174 /// an uninitialized field is found within the region.
175 bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain);
176
177 /// This method checks a region of a pointer or reference object, and returns
178 /// true if the ptr/ref object itself or any field within the pointee's region
179 /// is uninitialized.
180 bool isPointerOrReferenceUninit(const FieldRegion *FR,
181 FieldChainInfo LocalChain);
182
183 /// This method checks a region of MemberPointerType, and returns true if the
184 /// the pointer is uninitialized.
185 bool isMemberPointerUninit(const FieldRegion *FR, FieldChainInfo LocalChain);
186
187 /// This method returns true if the value of a primitive object is
188 /// uninitialized.
189 bool isPrimitiveUninit(const SVal &V);
190
191 // Note that we don't have a method for arrays -- the elements of an array are
192 // often left uninitialized intentionally even when it is of a C++ record
193 // type, so we'll assume that an array is always initialized.
194 // TODO: Add a support for nonloc::LocAsInteger.
195};
196
197// Utility function declarations.
198
199/// Returns the object that was constructed by CtorDecl, or None if that isn't
200/// possible.
201Optional<nonloc::LazyCompoundVal>
202getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context);
203
204/// Checks whether the constructor under checking is called by another
205/// constructor.
206bool isCalledByConstructor(const CheckerContext &Context);
207
208/// Returns whether FD can be (transitively) dereferenced to a void pointer type
209/// (void*, void**, ...). The type of the region behind a void pointer isn't
210/// known, and thus FD can not be analyzed.
211bool isVoidPointer(const FieldDecl *FD);
212
213/// Returns true if T is a primitive type. We'll call a type primitive if it's
214/// either a BuiltinType or an EnumeralType.
215bool isPrimitiveType(const QualType &T) {
216 return T->isBuiltinType() || T->isEnumeralType();
217}
218
219} // end of anonymous namespace
220
221//===----------------------------------------------------------------------===//
222// Methods for UninitializedObjectChecker.
223//===----------------------------------------------------------------------===//
224
225void UninitializedObjectChecker::checkEndFunction(
226 CheckerContext &Context) const {
227
228 const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
229 Context.getLocationContext()->getDecl());
230 if (!CtorDecl)
231 return;
232
233 if (!CtorDecl->isUserProvided())
234 return;
235
236 if (CtorDecl->getParent()->isUnion())
237 return;
238
239 // This avoids essentially the same error being reported multiple times.
240 if (isCalledByConstructor(Context))
241 return;
242
243 Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context);
244 if (!Object)
245 return;
246
247 FindUninitializedFields F(Context.getState(), Object->getRegion(),
248 IsPedantic);
249
250 const UninitFieldSet &UninitFields = F.getUninitFields();
251
252 if (UninitFields.empty())
253 return;
254
255 // There are uninitialized fields in the record.
256
257 ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState());
258 if (!Node)
259 return;
260
261 PathDiagnosticLocation LocUsedForUniqueing;
262 const Stmt *CallSite = Context.getStackFrame()->getCallSite();
263 if (CallSite)
264 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
265 CallSite, Context.getSourceManager(), Node->getLocationContext());
266
267 SmallString<100> WarningBuf;
268 llvm::raw_svector_ostream WarningOS(WarningBuf);
269 WarningOS << UninitFields.size() << " uninitialized field"
270 << (UninitFields.size() == 1 ? "" : "s")
271 << " at the end of the constructor call";
272
273 auto Report = llvm::make_unique<BugReport>(
274 *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
275 Node->getLocationContext()->getDecl());
276
277 // TODO: As of now, one warning is emitted per constructor call, and the
278 // uninitialized fields are listed in notes. Until there's a better support
279 // for notes avaible, a note-less version of this checker should be
280 // implemented.
281 for (const auto &FieldChain : UninitFields) {
282 SmallString<200> NoteBuf;
283 llvm::raw_svector_ostream NoteOS(NoteBuf);
284
285 if (FieldChain.isPointer()) {
286 if (FieldChain.isDereferenced())
287 NoteOS << "uninitialized pointee 'this->";
288 else
289 NoteOS << "uninitialized pointer 'this->";
290 } else
291 NoteOS << "uninitialized field 'this->";
292 FieldChain.print(NoteOS);
293 NoteOS << "'";
294
295 Report->addNote(NoteOS.str(),
296 PathDiagnosticLocation::create(FieldChain.getEndOfChain(),
297 Context.getSourceManager()));
298 }
299
300 Context.emitReport(std::move(Report));
301}
302
303//===----------------------------------------------------------------------===//
304// Methods for FindUninitializedFields.
305//===----------------------------------------------------------------------===//
306
307FindUninitializedFields::FindUninitializedFields(
308 ProgramStateRef State, const TypedValueRegion *const R, bool IsPedantic)
309 : State(State), ObjectR(R), IsPedantic(IsPedantic) {}
310
311const UninitFieldSet &FindUninitializedFields::getUninitFields() {
312 isNonUnionUninit(ObjectR, FieldChainInfo());
313
314 if (!IsPedantic && !IsAnyFieldInitialized)
315 UninitFields.clear();
316
317 return UninitFields;
318}
319
320bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) {
321 if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
322 Chain.getEndOfChain()->getLocation()))
323 return false;
324
325 return UninitFields.insert(Chain).second;
326}
327
328bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
329 FieldChainInfo LocalChain) {
330 assert(R->getValueType()->isRecordType() &&
331 !R->getValueType()->isUnionType() &&
332 "This method only checks non-union record objects!");
333
334 const RecordDecl *RD =
335 R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition();
336 assert(RD && "Referred record has no definition");
337
338 bool ContainsUninitField = false;
339
340 // Are all of this non-union's fields initialized?
341 for (const FieldDecl *I : RD->fields()) {
342
343 const auto FieldVal =
344 State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
345 const auto *FR = FieldVal.getRegionAs<FieldRegion>();
346 QualType T = I->getType();
347
348 // If LocalChain already contains FR, then we encountered a cyclic
349 // reference. In this case, region FR is already under checking at an
350 // earlier node in the directed tree.
351 if (LocalChain.contains(FR))
352 return false;
353
354 if (T->isStructureOrClassType()) {
355 if (isNonUnionUninit(FR, {LocalChain, FR}))
356 ContainsUninitField = true;
357 continue;
358 }
359
360 if (T->isUnionType()) {
361 if (isUnionUninit(FR)) {
362 if (addFieldToUninits({LocalChain, FR}))
363 ContainsUninitField = true;
364 } else
365 IsAnyFieldInitialized = true;
366 continue;
367 }
368
369 if (T->isArrayType()) {
370 IsAnyFieldInitialized = true;
371 continue;
372 }
373
374 if (T->isMemberPointerType()) {
375 if (isMemberPointerUninit(FR, LocalChain))
376 ContainsUninitField = true;
377 continue;
378 }
379
380 // If this is a pointer or reference type.
381 if (T->isPointerType() || T->isReferenceType()) {
382 if (isPointerOrReferenceUninit(FR, LocalChain))
383 ContainsUninitField = true;
384 continue;
385 }
386
387 assert(isPrimitiveType(T) && "Non-primitive type! "
388 "At this point FR must be primitive!");
389
390 SVal V = State->getSVal(FieldVal);
391
392 if (isPrimitiveUninit(V)) {
393 if (addFieldToUninits({LocalChain, FR}))
394 ContainsUninitField = true;
395 }
396 }
397
398 // Checking bases.
399 // FIXME: As of now, because of `isCalledByConstructor`, objects whose type
400 // is a descendant of another type will emit warnings for uninitalized
401 // inherited members.
402 // This is not the only way to analyze bases of an object -- if we didn't
403 // filter them out, and didn't analyze the bases, this checker would run for
404 // each base of the object in order of base initailization and in theory would
405 // find every uninitalized field. This approach could also make handling
406 // diamond inheritances more easily.
407 //
408 // This rule (that a descendant type's cunstructor is responsible for
409 // initializing inherited data members) is not obvious, and should it should
410 // be.
411 const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
412 if (!CXXRD)
413 return ContainsUninitField;
414
415 for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
416 const auto *BaseRegion = State->getLValue(BaseSpec, R)
417 .castAs<loc::MemRegionVal>()
418 .getRegionAs<TypedValueRegion>();
419
420 if (isNonUnionUninit(BaseRegion, LocalChain))
421 ContainsUninitField = true;
422 }
423
424 return ContainsUninitField;
425}
426
427bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
428 assert(R->getValueType()->isUnionType() &&
429 "This method only checks union objects!");
430 // TODO: Implement support for union fields.
431 return false;
432}
433
434// Note that pointers/references don't contain fields themselves, so in this
435// function we won't add anything to LocalChain.
436bool FindUninitializedFields::isPointerOrReferenceUninit(
437 const FieldRegion *FR, FieldChainInfo LocalChain) {
438
439 assert((FR->getDecl()->getType()->isPointerType() ||
440 FR->getDecl()->getType()->isReferenceType()) &&
441 "This method only checks pointer/reference objects!");
442
443 SVal V = State->getSVal(FR);
444
445 if (V.isUnknown() || V.isZeroConstant()) {
446 IsAnyFieldInitialized = true;
447 return false;
448 }
449
450 if (V.isUndef()) {
451 return addFieldToUninits({LocalChain, FR});
452 }
453
454 const FieldDecl *FD = FR->getDecl();
455
456 // TODO: The dynamic type of a void pointer may be retrieved with
457 // `getDynamicTypeInfo`.
458 if (isVoidPointer(FD)) {
459 IsAnyFieldInitialized = true;
460 return false;
461 }
462
463 assert(V.getAs<Loc>() && "V should be Loc at this point!");
464
465 // At this point the pointer itself is initialized and points to a valid
466 // location, we'll now check the pointee.
467 SVal DerefdV = State->getSVal(V.castAs<Loc>());
468
469 // TODO: Dereferencing should be done according to the dynamic type.
470 while (Optional<Loc> L = DerefdV.getAs<Loc>()) {
471 DerefdV = State->getSVal(*L);
472 }
473
474 // If V is a pointer pointing to a record type.
475 if (Optional<nonloc::LazyCompoundVal> RecordV =
476 DerefdV.getAs<nonloc::LazyCompoundVal>()) {
477
478 const TypedValueRegion *R = RecordV->getRegion();
479
480 // We can't reason about symbolic regions, assume its initialized.
481 // Note that this also avoids a potential infinite recursion, because
482 // constructors for list-like classes are checked without being called, and
483 // the Static Analyzer will construct a symbolic region for Node *next; or
484 // similar code snippets.
485 if (R->getSymbolicBase()) {
486 IsAnyFieldInitialized = true;
487 return false;
488 }
489
490 const QualType T = R->getValueType();
491
492 if (T->isStructureOrClassType())
493 return isNonUnionUninit(R, {LocalChain, FR});
494
495 if (T->isUnionType()) {
496 if (isUnionUninit(R)) {
497 return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
498 } else {
499 IsAnyFieldInitialized = true;
500 return false;
501 }
502 }
503
504 if (T->isArrayType()) {
505 IsAnyFieldInitialized = true;
506 return false;
507 }
508
509 llvm_unreachable("All cases are handled!");
510 }
511
512 // TODO: If possible, it should be asserted that the DerefdV at this point is
513 // primitive.
514
515 if (isPrimitiveUninit(DerefdV))
516 return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
517
518 IsAnyFieldInitialized = true;
519 return false;
520}
521
522bool FindUninitializedFields::isMemberPointerUninit(const FieldRegion *FR,
523 FieldChainInfo LocalChain) {
524 assert(FR->getDecl()->getType()->isMemberPointerType() &&
525 "This function only checks regions that hold MemberPointerTypes!");
526 // TODO: Implement support for MemberPointerTypes.
527 return false;
528}
529
530bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
531 if (V.isUndef())
532 return true;
533
534 IsAnyFieldInitialized = true;
535 return false;
536}
537
538//===----------------------------------------------------------------------===//
539// Methods for FieldChainInfo.
540//===----------------------------------------------------------------------===//
541
542FieldChainInfo::FieldChainInfo(const FieldChainInfo &Other,
543 const FieldRegion *FR, const bool IsDereferenced)
544 : FieldChainInfo(Other, IsDereferenced) {
545 assert(!contains(FR) && "Can't add a field that is already a part of the "
546 "fieldchain! Is this a cyclic reference?");
547 Chain = Factory.add(FR, Other.Chain);
548}
549
550bool FieldChainInfo::isPointer() const {
551 assert(!Chain.isEmpty() && "Empty fieldchain!");
552 return (*Chain.begin())->getDecl()->getType()->isPointerType();
553}
554
555bool FieldChainInfo::isDereferenced() const {
556 assert(isPointer() && "Only pointers may or may not be dereferenced!");
557 return IsDereferenced;
558}
559
560const FieldDecl *FieldChainInfo::getEndOfChain() const {
561 assert(!Chain.isEmpty() && "Empty fieldchain!");
562 return (*Chain.begin())->getDecl();
563}
564
565// TODO: This function constructs an incorrect fieldchain string in the
566// following case:
567//
568// struct Base { int x; };
569// struct D1 : Base {}; struct D2 : Base {};
570//
571// struct MostDerived : D1, D2 {
572// MostDerived() {}
573// }
574//
575// A call to MostDerived::MostDerived() will cause two notes that say
576// "uninitialized field 'this->x'", but we can't refer to 'x' directly,
577// we need an explicit namespace resolution whether the uninit field was
578// 'D1::x' or 'D2::x'.
579//
580// TODO: If a field in the fieldchain is a captured lambda parameter, this
581// function constructs an empty string for it:
582//
583// template <class Callable> struct A {
584// Callable c;
585// A(const Callable &c, int) : c(c) {}
586// };
587//
588// int b; // say that this isn't zero initialized
589// auto alwaysTrue = [&b](int a) { return true; };
590//
591// A call with these parameters: A<decltype(alwaysTrue)>::A(alwaysTrue, int())
592// will emit a note with the message "uninitialized field: 'this->c.'". If
593// possible, the lambda parameter name should be retrieved or be replaced with a
594// "<lambda parameter>" or something similar.
595void FieldChainInfo::print(llvm::raw_ostream &Out) const {
596 if (Chain.isEmpty())
597 return;
598
599 const llvm::ImmutableListImpl<const FieldRegion *> *L =
600 Chain.getInternalPointer();
601 printTail(Out, L->getTail());
602 Out << L->getHead()->getDecl()->getNameAsString();
603}
604
605void FieldChainInfo::printTail(
606 llvm::raw_ostream &Out,
607 const llvm::ImmutableListImpl<const FieldRegion *> *L) {
608 if (!L)
609 return;
610
611 printTail(Out, L->getTail());
612 const FieldDecl *Field = L->getHead()->getDecl();
613 Out << Field->getNameAsString();
614 Out << (Field->getType()->isPointerType() ? "->" : ".");
615}
616
617//===----------------------------------------------------------------------===//
618// Utility functions.
619//===----------------------------------------------------------------------===//
620
621namespace {
622
623bool isVoidPointer(const FieldDecl *FD) {
624 QualType T = FD->getType();
625
626 while (!T.isNull()) {
627 if (T->isVoidPointerType())
628 return true;
629 T = T->getPointeeType();
630 }
631 return false;
632}
633
634Optional<nonloc::LazyCompoundVal>
635getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) {
636
637 Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(),
638 Context.getStackFrame());
639 // Getting the value for 'this'.
640 SVal This = Context.getState()->getSVal(ThisLoc);
641
642 // Getting the value for '*this'.
643 SVal Object = Context.getState()->getSVal(This.castAs<Loc>());
644
645 return Object.getAs<nonloc::LazyCompoundVal>();
646}
647
648// TODO: We should also check that if the constructor was called by another
649// constructor, whether those two are in any relation to one another. In it's
650// current state, this introduces some false negatives.
651bool isCalledByConstructor(const CheckerContext &Context) {
652 const LocationContext *LC = Context.getLocationContext()->getParent();
653
654 while (LC) {
655 if (isa<CXXConstructorDecl>(LC->getDecl()))
656 return true;
657
658 LC = LC->getParent();
659 }
660 return false;
661}
662
663} // end of anonymous namespace
664
665void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
666 auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
667 Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
668 "Pedantic", /*DefaultVal*/ false, Chk);
669}