blob: ad1a89e71bfb121a012b8b9dce5515568a90d249 [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//
Kristof Umanna3f7b582018-08-07 12:55:26 +000013// This checker has several options:
Kristof Umann9bd44392018-06-29 11:25:24 +000014// - "Pedantic" (boolean). If its not set or is set to false, the checker
15// won't emit warnings for objects that don't have at least one initialized
16// field. This may be set with
17//
Kristof Umanna3f7b582018-08-07 12:55:26 +000018// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`.
Kristof Umann9bd44392018-06-29 11:25:24 +000019//
20// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
21// warning for each uninitalized field, as opposed to emitting one warning
22// per constructor call, and listing the uninitialized fields that belongs
23// to it in notes. Defaults to false.
24//
Kristof Umanna3f7b582018-08-07 12:55:26 +000025// `-analyzer-config \
26// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
27//
28// - "CheckPointeeInitialization" (boolean). If set to false, the checker will
29// not analyze the pointee of pointer/reference fields, and will only check
30// whether the object itself is initialized. Defaults to false.
31//
32// `-analyzer-config \
33// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
34//
35// TODO: With some clever heuristics, some pointers should be dereferenced
36// by default. For example, if the pointee is constructed within the
37// constructor call, it's reasonable to say that no external object
38// references it, and we wouldn't generate multiple report on the same
39// pointee.
Kristof Umann30f08652018-06-18 11:50:17 +000040//
Kristof Umann56963ae2018-08-13 18:17:05 +000041// To read about how the checker works, refer to the comments in
42// UninitializedObject.h.
43//
44// Some of the logic is implemented in UninitializedPointee.cpp, to reduce the
45// complexity of this file.
46//
Kristof Umann30f08652018-06-18 11:50:17 +000047//===----------------------------------------------------------------------===//
48
49#include "ClangSACheckers.h"
Kristof Umanna37bba42018-08-13 18:22:22 +000050#include "UninitializedObject.h"
Kristof Umann30f08652018-06-18 11:50:17 +000051#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
52#include "clang/StaticAnalyzer/Core/Checker.h"
53#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Kristof Umannef9af052018-08-08 13:18:53 +000054#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
Kristof Umann30f08652018-06-18 11:50:17 +000055
56using namespace clang;
57using namespace clang::ento;
58
59namespace {
60
61class UninitializedObjectChecker : public Checker<check::EndFunction> {
62 std::unique_ptr<BuiltinBug> BT_uninitField;
63
64public:
Kristof Umann9bd44392018-06-29 11:25:24 +000065 // These fields will be initialized when registering the checker.
66 bool IsPedantic;
67 bool ShouldConvertNotesToWarnings;
Kristof Umanna3f7b582018-08-07 12:55:26 +000068 bool CheckPointeeInitialization;
Kristof Umann30f08652018-06-18 11:50:17 +000069
70 UninitializedObjectChecker()
71 : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
Reka Kovacsed8c05c2018-07-16 20:47:45 +000072 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
Kristof Umann30f08652018-06-18 11:50:17 +000073};
74
Kristof Umanncc852442018-07-12 13:13:46 +000075} // end of anonymous namespace
76
Kristof Umann30f08652018-06-18 11:50:17 +000077// Utility function declarations.
78
79/// Returns the object that was constructed by CtorDecl, or None if that isn't
80/// possible.
Kristof Umann0735cfb2018-08-08 12:23:02 +000081// TODO: Refactor this function so that it returns the constructed object's
82// region.
Kristof Umanncc852442018-07-12 13:13:46 +000083static Optional<nonloc::LazyCompoundVal>
Kristof Umann30f08652018-06-18 11:50:17 +000084getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context);
85
Kristof Umann0735cfb2018-08-08 12:23:02 +000086/// Checks whether the object constructed by \p Ctor will be analyzed later
87/// (e.g. if the object is a field of another object, in which case we'd check
88/// it multiple times).
89static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
Kristof Umanna37bba42018-08-13 18:22:22 +000090 CheckerContext &Context);
Kristof Umann30f08652018-06-18 11:50:17 +000091
Kristof Umann9bd44392018-06-29 11:25:24 +000092/// Constructs a note message for a given FieldChainInfo object.
Kristof Umanncc852442018-07-12 13:13:46 +000093static void printNoteMessage(llvm::raw_ostream &Out,
94 const FieldChainInfo &Chain);
Kristof Umann30f08652018-06-18 11:50:17 +000095
Kristof Umann8c119092018-07-13 12:54:47 +000096/// Returns with Field's name. This is a helper function to get the correct name
97/// even if Field is a captured lambda variable.
98static StringRef getVariableName(const FieldDecl *Field);
99
Kristof Umann30f08652018-06-18 11:50:17 +0000100//===----------------------------------------------------------------------===//
101// Methods for UninitializedObjectChecker.
102//===----------------------------------------------------------------------===//
103
104void UninitializedObjectChecker::checkEndFunction(
Reka Kovacsed8c05c2018-07-16 20:47:45 +0000105 const ReturnStmt *RS, CheckerContext &Context) const {
Kristof Umann30f08652018-06-18 11:50:17 +0000106
107 const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
108 Context.getLocationContext()->getDecl());
109 if (!CtorDecl)
110 return;
111
112 if (!CtorDecl->isUserProvided())
113 return;
114
115 if (CtorDecl->getParent()->isUnion())
116 return;
117
118 // This avoids essentially the same error being reported multiple times.
Kristof Umann0735cfb2018-08-08 12:23:02 +0000119 if (willObjectBeAnalyzedLater(CtorDecl, Context))
Kristof Umann30f08652018-06-18 11:50:17 +0000120 return;
121
122 Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context);
123 if (!Object)
124 return;
125
Kristof Umanna3f7b582018-08-07 12:55:26 +0000126 FindUninitializedFields F(Context.getState(), Object->getRegion(), IsPedantic,
127 CheckPointeeInitialization);
Kristof Umann30f08652018-06-18 11:50:17 +0000128
129 const UninitFieldSet &UninitFields = F.getUninitFields();
130
131 if (UninitFields.empty())
132 return;
133
134 // There are uninitialized fields in the record.
135
136 ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState());
137 if (!Node)
138 return;
139
140 PathDiagnosticLocation LocUsedForUniqueing;
141 const Stmt *CallSite = Context.getStackFrame()->getCallSite();
142 if (CallSite)
143 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
144 CallSite, Context.getSourceManager(), Node->getLocationContext());
145
Kristof Umann9bd44392018-06-29 11:25:24 +0000146 // For Plist consumers that don't support notes just yet, we'll convert notes
147 // to warnings.
148 if (ShouldConvertNotesToWarnings) {
149 for (const auto &Chain : UninitFields) {
150 SmallString<100> WarningBuf;
151 llvm::raw_svector_ostream WarningOS(WarningBuf);
152
153 printNoteMessage(WarningOS, Chain);
154
155 auto Report = llvm::make_unique<BugReport>(
156 *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
157 Node->getLocationContext()->getDecl());
158 Context.emitReport(std::move(Report));
159 }
160 return;
161 }
162
Kristof Umann30f08652018-06-18 11:50:17 +0000163 SmallString<100> WarningBuf;
164 llvm::raw_svector_ostream WarningOS(WarningBuf);
165 WarningOS << UninitFields.size() << " uninitialized field"
166 << (UninitFields.size() == 1 ? "" : "s")
167 << " at the end of the constructor call";
168
169 auto Report = llvm::make_unique<BugReport>(
170 *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
171 Node->getLocationContext()->getDecl());
172
Kristof Umann9bd44392018-06-29 11:25:24 +0000173 for (const auto &Chain : UninitFields) {
Kristof Umann30f08652018-06-18 11:50:17 +0000174 SmallString<200> NoteBuf;
175 llvm::raw_svector_ostream NoteOS(NoteBuf);
176
Kristof Umann9bd44392018-06-29 11:25:24 +0000177 printNoteMessage(NoteOS, Chain);
Kristof Umann30f08652018-06-18 11:50:17 +0000178
179 Report->addNote(NoteOS.str(),
Kristof Umann9bd44392018-06-29 11:25:24 +0000180 PathDiagnosticLocation::create(Chain.getEndOfChain(),
Kristof Umann30f08652018-06-18 11:50:17 +0000181 Context.getSourceManager()));
182 }
Kristof Umann30f08652018-06-18 11:50:17 +0000183 Context.emitReport(std::move(Report));
184}
185
186//===----------------------------------------------------------------------===//
187// Methods for FindUninitializedFields.
188//===----------------------------------------------------------------------===//
189
190FindUninitializedFields::FindUninitializedFields(
Kristof Umanna3f7b582018-08-07 12:55:26 +0000191 ProgramStateRef State, const TypedValueRegion *const R, bool IsPedantic,
192 bool CheckPointeeInitialization)
193 : State(State), ObjectR(R), IsPedantic(IsPedantic),
194 CheckPointeeInitialization(CheckPointeeInitialization) {}
Kristof Umann30f08652018-06-18 11:50:17 +0000195
196const UninitFieldSet &FindUninitializedFields::getUninitFields() {
Kristof Umannea7cb672018-08-13 17:55:52 +0000197 isNonUnionUninit(ObjectR, FieldChainInfo(Factory));
Kristof Umann30f08652018-06-18 11:50:17 +0000198
199 if (!IsPedantic && !IsAnyFieldInitialized)
200 UninitFields.clear();
201
202 return UninitFields;
203}
204
205bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) {
206 if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
207 Chain.getEndOfChain()->getLocation()))
208 return false;
209
210 return UninitFields.insert(Chain).second;
211}
212
213bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
214 FieldChainInfo LocalChain) {
215 assert(R->getValueType()->isRecordType() &&
216 !R->getValueType()->isUnionType() &&
217 "This method only checks non-union record objects!");
218
219 const RecordDecl *RD =
220 R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition();
221 assert(RD && "Referred record has no definition");
222
223 bool ContainsUninitField = false;
224
225 // Are all of this non-union's fields initialized?
226 for (const FieldDecl *I : RD->fields()) {
227
228 const auto FieldVal =
229 State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
230 const auto *FR = FieldVal.getRegionAs<FieldRegion>();
231 QualType T = I->getType();
232
233 // If LocalChain already contains FR, then we encountered a cyclic
234 // reference. In this case, region FR is already under checking at an
235 // earlier node in the directed tree.
236 if (LocalChain.contains(FR))
237 return false;
238
239 if (T->isStructureOrClassType()) {
240 if (isNonUnionUninit(FR, {LocalChain, FR}))
241 ContainsUninitField = true;
242 continue;
243 }
244
245 if (T->isUnionType()) {
246 if (isUnionUninit(FR)) {
247 if (addFieldToUninits({LocalChain, FR}))
248 ContainsUninitField = true;
249 } else
250 IsAnyFieldInitialized = true;
251 continue;
252 }
253
254 if (T->isArrayType()) {
255 IsAnyFieldInitialized = true;
256 continue;
257 }
258
George Karpenkovcf40ba82018-08-09 19:03:12 +0000259 if (T->isPointerType() || T->isReferenceType() || T->isBlockPointerType()) {
Kristof Umann30f08652018-06-18 11:50:17 +0000260 if (isPointerOrReferenceUninit(FR, LocalChain))
261 ContainsUninitField = true;
262 continue;
263 }
264
Kristof Umann20e85ba2018-06-19 08:35:02 +0000265 if (isPrimitiveType(T)) {
266 SVal V = State->getSVal(FieldVal);
Kristof Umann30f08652018-06-18 11:50:17 +0000267
Kristof Umann20e85ba2018-06-19 08:35:02 +0000268 if (isPrimitiveUninit(V)) {
269 if (addFieldToUninits({LocalChain, FR}))
270 ContainsUninitField = true;
271 }
272 continue;
Kristof Umann30f08652018-06-18 11:50:17 +0000273 }
Kristof Umann20e85ba2018-06-19 08:35:02 +0000274
275 llvm_unreachable("All cases are handled!");
Kristof Umann30f08652018-06-18 11:50:17 +0000276 }
277
278 // Checking bases.
Kristof Umann0735cfb2018-08-08 12:23:02 +0000279 // FIXME: As of now, because of `willObjectBeAnalyzedLater`, objects whose
280 // type is a descendant of another type will emit warnings for uninitalized
Kristof Umann30f08652018-06-18 11:50:17 +0000281 // inherited members.
282 // This is not the only way to analyze bases of an object -- if we didn't
283 // filter them out, and didn't analyze the bases, this checker would run for
284 // each base of the object in order of base initailization and in theory would
285 // find every uninitalized field. This approach could also make handling
286 // diamond inheritances more easily.
287 //
288 // This rule (that a descendant type's cunstructor is responsible for
289 // initializing inherited data members) is not obvious, and should it should
290 // be.
291 const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
292 if (!CXXRD)
293 return ContainsUninitField;
294
295 for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
296 const auto *BaseRegion = State->getLValue(BaseSpec, R)
297 .castAs<loc::MemRegionVal>()
298 .getRegionAs<TypedValueRegion>();
299
300 if (isNonUnionUninit(BaseRegion, LocalChain))
301 ContainsUninitField = true;
302 }
303
304 return ContainsUninitField;
305}
306
307bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
308 assert(R->getValueType()->isUnionType() &&
309 "This method only checks union objects!");
310 // TODO: Implement support for union fields.
311 return false;
312}
313
Kristof Umann30f08652018-06-18 11:50:17 +0000314bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
315 if (V.isUndef())
316 return true;
317
318 IsAnyFieldInitialized = true;
319 return false;
320}
321
322//===----------------------------------------------------------------------===//
323// Methods for FieldChainInfo.
324//===----------------------------------------------------------------------===//
325
326FieldChainInfo::FieldChainInfo(const FieldChainInfo &Other,
327 const FieldRegion *FR, const bool IsDereferenced)
328 : FieldChainInfo(Other, IsDereferenced) {
329 assert(!contains(FR) && "Can't add a field that is already a part of the "
330 "fieldchain! Is this a cyclic reference?");
331 Chain = Factory.add(FR, Other.Chain);
332}
333
334bool FieldChainInfo::isPointer() const {
335 assert(!Chain.isEmpty() && "Empty fieldchain!");
336 return (*Chain.begin())->getDecl()->getType()->isPointerType();
337}
338
339bool FieldChainInfo::isDereferenced() const {
340 assert(isPointer() && "Only pointers may or may not be dereferenced!");
341 return IsDereferenced;
342}
343
344const FieldDecl *FieldChainInfo::getEndOfChain() const {
345 assert(!Chain.isEmpty() && "Empty fieldchain!");
346 return (*Chain.begin())->getDecl();
347}
348
Kristof Umanna37bba42018-08-13 18:22:22 +0000349/// Prints every element except the last to `Out`. Since ImmutableLists store
350/// elements in reverse order, and have no reverse iterators, we use a
351/// recursive function to print the fieldchain correctly. The last element in
352/// the chain is to be printed by `print`.
353static void printTail(llvm::raw_ostream &Out,
354 const FieldChainInfo::FieldChainImpl *L);
355
Kristof Umannef9af052018-08-08 13:18:53 +0000356// TODO: This function constructs an incorrect string if a void pointer is a
357// part of the chain:
358//
359// struct B { int x; }
360//
361// struct A {
362// void *vptr;
363// A(void* vptr) : vptr(vptr) {}
364// };
365//
366// void f() {
367// B b;
368// A a(&b);
369// }
370//
371// The note message will be "uninitialized field 'this->vptr->x'", even though
372// void pointers can't be dereferenced. This should be changed to "uninitialized
373// field 'static_cast<B*>(this->vptr)->x'".
374//
Kristof Umann30f08652018-06-18 11:50:17 +0000375// TODO: This function constructs an incorrect fieldchain string in the
376// following case:
377//
378// struct Base { int x; };
379// struct D1 : Base {}; struct D2 : Base {};
380//
381// struct MostDerived : D1, D2 {
382// MostDerived() {}
383// }
384//
385// A call to MostDerived::MostDerived() will cause two notes that say
386// "uninitialized field 'this->x'", but we can't refer to 'x' directly,
387// we need an explicit namespace resolution whether the uninit field was
388// 'D1::x' or 'D2::x'.
Kristof Umann30f08652018-06-18 11:50:17 +0000389void FieldChainInfo::print(llvm::raw_ostream &Out) const {
390 if (Chain.isEmpty())
391 return;
392
Kristof Umanna37bba42018-08-13 18:22:22 +0000393 const FieldChainImpl *L = Chain.getInternalPointer();
Kristof Umann30f08652018-06-18 11:50:17 +0000394 printTail(Out, L->getTail());
Kristof Umann8c119092018-07-13 12:54:47 +0000395 Out << getVariableName(L->getHead()->getDecl());
Kristof Umann30f08652018-06-18 11:50:17 +0000396}
397
Kristof Umanna37bba42018-08-13 18:22:22 +0000398static void printTail(llvm::raw_ostream &Out,
399 const FieldChainInfo::FieldChainImpl *L) {
Kristof Umann30f08652018-06-18 11:50:17 +0000400 if (!L)
401 return;
402
403 printTail(Out, L->getTail());
404 const FieldDecl *Field = L->getHead()->getDecl();
Kristof Umann8c119092018-07-13 12:54:47 +0000405 Out << getVariableName(Field);
Kristof Umann30f08652018-06-18 11:50:17 +0000406 Out << (Field->getType()->isPointerType() ? "->" : ".");
407}
408
409//===----------------------------------------------------------------------===//
410// Utility functions.
411//===----------------------------------------------------------------------===//
412
Kristof Umanncc852442018-07-12 13:13:46 +0000413static Optional<nonloc::LazyCompoundVal>
Kristof Umann30f08652018-06-18 11:50:17 +0000414getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) {
415
416 Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(),
417 Context.getStackFrame());
418 // Getting the value for 'this'.
419 SVal This = Context.getState()->getSVal(ThisLoc);
420
421 // Getting the value for '*this'.
422 SVal Object = Context.getState()->getSVal(This.castAs<Loc>());
423
424 return Object.getAs<nonloc::LazyCompoundVal>();
425}
426
Kristof Umann0735cfb2018-08-08 12:23:02 +0000427static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
Kristof Umanna37bba42018-08-13 18:22:22 +0000428 CheckerContext &Context) {
Kristof Umann30f08652018-06-18 11:50:17 +0000429
Kristof Umann0735cfb2018-08-08 12:23:02 +0000430 Optional<nonloc::LazyCompoundVal> CurrentObject = getObjectVal(Ctor, Context);
431 if (!CurrentObject)
432 return false;
433
434 const LocationContext *LC = Context.getLocationContext();
435 while ((LC = LC->getParent())) {
436
437 // If \p Ctor was called by another constructor.
438 const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
439 if (!OtherCtor)
440 continue;
441
442 Optional<nonloc::LazyCompoundVal> OtherObject =
443 getObjectVal(OtherCtor, Context);
444 if (!OtherObject)
445 continue;
446
447 // If the CurrentObject is a subregion of OtherObject, it will be analyzed
448 // during the analysis of OtherObject.
449 if (CurrentObject->getRegion()->isSubRegionOf(OtherObject->getRegion()))
Kristof Umann30f08652018-06-18 11:50:17 +0000450 return true;
Kristof Umann30f08652018-06-18 11:50:17 +0000451 }
Kristof Umann0735cfb2018-08-08 12:23:02 +0000452
Kristof Umann30f08652018-06-18 11:50:17 +0000453 return false;
454}
455
Kristof Umanncc852442018-07-12 13:13:46 +0000456static void printNoteMessage(llvm::raw_ostream &Out,
457 const FieldChainInfo &Chain) {
Kristof Umann9bd44392018-06-29 11:25:24 +0000458 if (Chain.isPointer()) {
459 if (Chain.isDereferenced())
460 Out << "uninitialized pointee 'this->";
461 else
462 Out << "uninitialized pointer 'this->";
463 } else
464 Out << "uninitialized field 'this->";
465 Chain.print(Out);
466 Out << "'";
467}
468
Kristof Umann8c119092018-07-13 12:54:47 +0000469static StringRef getVariableName(const FieldDecl *Field) {
470 // If Field is a captured lambda variable, Field->getName() will return with
471 // an empty string. We can however acquire it's name from the lambda's
472 // captures.
473 const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
474
475 if (CXXParent && CXXParent->isLambda()) {
476 assert(CXXParent->captures_begin());
477 auto It = CXXParent->captures_begin() + Field->getFieldIndex();
478 return It->getCapturedVar()->getName();
479 }
480
481 return Field->getName();
482}
483
Kristof Umann30f08652018-06-18 11:50:17 +0000484void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
485 auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
486 Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
487 "Pedantic", /*DefaultVal*/ false, Chk);
Kristof Umann9bd44392018-06-29 11:25:24 +0000488 Chk->ShouldConvertNotesToWarnings = Mgr.getAnalyzerOptions().getBooleanOption(
489 "NotesAsWarnings", /*DefaultVal*/ false, Chk);
Kristof Umanna3f7b582018-08-07 12:55:26 +0000490 Chk->CheckPointeeInitialization = Mgr.getAnalyzerOptions().getBooleanOption(
491 "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
Kristof Umann30f08652018-06-18 11:50:17 +0000492}