UninitializedObjectChecker.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. //===----- 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. // To read about command line options and how the checker works, refer to the
  14. // top of the file and inline comments in UninitializedObject.h.
  15. //
  16. // Some of the logic is implemented in UninitializedPointee.cpp, to reduce the
  17. // complexity of this file.
  18. //
  19. //===----------------------------------------------------------------------===//
  20. #include "../ClangSACheckers.h"
  21. #include "UninitializedObject.h"
  22. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  23. #include "clang/StaticAnalyzer/Core/Checker.h"
  24. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  25. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
  26. using namespace clang;
  27. using namespace clang::ento;
  28. namespace {
  29. class UninitializedObjectChecker : public Checker<check::EndFunction> {
  30. std::unique_ptr<BuiltinBug> BT_uninitField;
  31. public:
  32. // The fields of this struct will be initialized when registering the checker.
  33. UninitObjCheckerOptions Opts;
  34. UninitializedObjectChecker()
  35. : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
  36. void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
  37. };
  38. /// A basic field type, that is not a pointer or a reference, it's dynamic and
  39. /// static type is the same.
  40. class RegularField final : public FieldNode {
  41. public:
  42. RegularField(const FieldRegion *FR) : FieldNode(FR) {}
  43. virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
  44. Out << "uninitialized field ";
  45. }
  46. virtual void printPrefix(llvm::raw_ostream &Out) const override {}
  47. virtual void printNode(llvm::raw_ostream &Out) const override {
  48. Out << getVariableName(getDecl());
  49. }
  50. virtual void printSeparator(llvm::raw_ostream &Out) const override {
  51. Out << '.';
  52. }
  53. };
  54. /// Represents that the FieldNode that comes after this is declared in a base
  55. /// of the previous FieldNode. As such, this descendant doesn't wrap a
  56. /// FieldRegion, and is purely a tool to describe a relation between two other
  57. /// FieldRegion wrapping descendants.
  58. class BaseClass final : public FieldNode {
  59. const QualType BaseClassT;
  60. public:
  61. BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) {
  62. assert(!T.isNull());
  63. assert(T->getAsCXXRecordDecl());
  64. }
  65. virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
  66. llvm_unreachable("This node can never be the final node in the "
  67. "fieldchain!");
  68. }
  69. virtual void printPrefix(llvm::raw_ostream &Out) const override {}
  70. virtual void printNode(llvm::raw_ostream &Out) const override {
  71. Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";
  72. }
  73. virtual void printSeparator(llvm::raw_ostream &Out) const override {}
  74. virtual bool isBase() const override { return true; }
  75. };
  76. } // end of anonymous namespace
  77. // Utility function declarations.
  78. /// Returns the region that was constructed by CtorDecl, or nullptr if that
  79. /// isn't possible.
  80. static const TypedValueRegion *
  81. getConstructedRegion(const CXXConstructorDecl *CtorDecl,
  82. CheckerContext &Context);
  83. /// Checks whether the object constructed by \p Ctor will be analyzed later
  84. /// (e.g. if the object is a field of another object, in which case we'd check
  85. /// it multiple times).
  86. static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
  87. CheckerContext &Context);
  88. /// Checks whether RD contains a field with a name or type name that matches
  89. /// \p Pattern.
  90. static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
  91. //===----------------------------------------------------------------------===//
  92. // Methods for UninitializedObjectChecker.
  93. //===----------------------------------------------------------------------===//
  94. void UninitializedObjectChecker::checkEndFunction(
  95. const ReturnStmt *RS, CheckerContext &Context) const {
  96. const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
  97. Context.getLocationContext()->getDecl());
  98. if (!CtorDecl)
  99. return;
  100. if (!CtorDecl->isUserProvided())
  101. return;
  102. if (CtorDecl->getParent()->isUnion())
  103. return;
  104. // This avoids essentially the same error being reported multiple times.
  105. if (willObjectBeAnalyzedLater(CtorDecl, Context))
  106. return;
  107. const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context);
  108. if (!R)
  109. return;
  110. FindUninitializedFields F(Context.getState(), R, Opts);
  111. const UninitFieldMap &UninitFields = F.getUninitFields();
  112. if (UninitFields.empty())
  113. return;
  114. // There are uninitialized fields in the record.
  115. ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState());
  116. if (!Node)
  117. return;
  118. PathDiagnosticLocation LocUsedForUniqueing;
  119. const Stmt *CallSite = Context.getStackFrame()->getCallSite();
  120. if (CallSite)
  121. LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
  122. CallSite, Context.getSourceManager(), Node->getLocationContext());
  123. // For Plist consumers that don't support notes just yet, we'll convert notes
  124. // to warnings.
  125. if (Opts.ShouldConvertNotesToWarnings) {
  126. for (const auto &Pair : UninitFields) {
  127. auto Report = llvm::make_unique<BugReport>(
  128. *BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
  129. Node->getLocationContext()->getDecl());
  130. Context.emitReport(std::move(Report));
  131. }
  132. return;
  133. }
  134. SmallString<100> WarningBuf;
  135. llvm::raw_svector_ostream WarningOS(WarningBuf);
  136. WarningOS << UninitFields.size() << " uninitialized field"
  137. << (UninitFields.size() == 1 ? "" : "s")
  138. << " at the end of the constructor call";
  139. auto Report = llvm::make_unique<BugReport>(
  140. *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
  141. Node->getLocationContext()->getDecl());
  142. for (const auto &Pair : UninitFields) {
  143. Report->addNote(Pair.second,
  144. PathDiagnosticLocation::create(Pair.first->getDecl(),
  145. Context.getSourceManager()));
  146. }
  147. Context.emitReport(std::move(Report));
  148. }
  149. //===----------------------------------------------------------------------===//
  150. // Methods for FindUninitializedFields.
  151. //===----------------------------------------------------------------------===//
  152. FindUninitializedFields::FindUninitializedFields(
  153. ProgramStateRef State, const TypedValueRegion *const R,
  154. const UninitObjCheckerOptions &Opts)
  155. : State(State), ObjectR(R), Opts(Opts) {
  156. isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory));
  157. // In non-pedantic mode, if ObjectR doesn't contain a single initialized
  158. // field, we'll assume that Object was intentionally left uninitialized.
  159. if (!Opts.IsPedantic && !isAnyFieldInitialized())
  160. UninitFields.clear();
  161. }
  162. bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) {
  163. if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
  164. Chain.getUninitRegion()->getDecl()->getLocation()))
  165. return false;
  166. UninitFieldMap::mapped_type NoteMsgBuf;
  167. llvm::raw_svector_ostream OS(NoteMsgBuf);
  168. Chain.printNoteMsg(OS);
  169. return UninitFields
  170. .insert(std::make_pair(Chain.getUninitRegion(), std::move(NoteMsgBuf)))
  171. .second;
  172. }
  173. bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
  174. FieldChainInfo LocalChain) {
  175. assert(R->getValueType()->isRecordType() &&
  176. !R->getValueType()->isUnionType() &&
  177. "This method only checks non-union record objects!");
  178. const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition();
  179. if (!RD) {
  180. IsAnyFieldInitialized = true;
  181. return true;
  182. }
  183. if (!Opts.IgnoredRecordsWithFieldPattern.empty() &&
  184. shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) {
  185. IsAnyFieldInitialized = true;
  186. return false;
  187. }
  188. bool ContainsUninitField = false;
  189. // Are all of this non-union's fields initialized?
  190. for (const FieldDecl *I : RD->fields()) {
  191. const auto FieldVal =
  192. State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
  193. const auto *FR = FieldVal.getRegionAs<FieldRegion>();
  194. QualType T = I->getType();
  195. // If LocalChain already contains FR, then we encountered a cyclic
  196. // reference. In this case, region FR is already under checking at an
  197. // earlier node in the directed tree.
  198. if (LocalChain.contains(FR))
  199. return false;
  200. if (T->isStructureOrClassType()) {
  201. if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR))))
  202. ContainsUninitField = true;
  203. continue;
  204. }
  205. if (T->isUnionType()) {
  206. if (isUnionUninit(FR)) {
  207. if (addFieldToUninits(LocalChain.add(RegularField(FR))))
  208. ContainsUninitField = true;
  209. } else
  210. IsAnyFieldInitialized = true;
  211. continue;
  212. }
  213. if (T->isArrayType()) {
  214. IsAnyFieldInitialized = true;
  215. continue;
  216. }
  217. SVal V = State->getSVal(FieldVal);
  218. if (isDereferencableType(T) || V.getAs<nonloc::LocAsInteger>()) {
  219. if (isDereferencableUninit(FR, LocalChain))
  220. ContainsUninitField = true;
  221. continue;
  222. }
  223. if (isPrimitiveType(T)) {
  224. if (isPrimitiveUninit(V)) {
  225. if (addFieldToUninits(LocalChain.add(RegularField(FR))))
  226. ContainsUninitField = true;
  227. }
  228. continue;
  229. }
  230. llvm_unreachable("All cases are handled!");
  231. }
  232. // Checking bases. The checker will regard inherited data members as direct
  233. // fields.
  234. const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
  235. if (!CXXRD)
  236. return ContainsUninitField;
  237. for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
  238. const auto *BaseRegion = State->getLValue(BaseSpec, R)
  239. .castAs<loc::MemRegionVal>()
  240. .getRegionAs<TypedValueRegion>();
  241. // If the head of the list is also a BaseClass, we'll overwrite it to avoid
  242. // note messages like 'this->A::B::x'.
  243. if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()) {
  244. if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead(
  245. BaseClass(BaseSpec.getType()))))
  246. ContainsUninitField = true;
  247. } else {
  248. if (isNonUnionUninit(BaseRegion,
  249. LocalChain.add(BaseClass(BaseSpec.getType()))))
  250. ContainsUninitField = true;
  251. }
  252. }
  253. return ContainsUninitField;
  254. }
  255. bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
  256. assert(R->getValueType()->isUnionType() &&
  257. "This method only checks union objects!");
  258. // TODO: Implement support for union fields.
  259. return false;
  260. }
  261. bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
  262. if (V.isUndef())
  263. return true;
  264. IsAnyFieldInitialized = true;
  265. return false;
  266. }
  267. //===----------------------------------------------------------------------===//
  268. // Methods for FieldChainInfo.
  269. //===----------------------------------------------------------------------===//
  270. bool FieldChainInfo::contains(const FieldRegion *FR) const {
  271. for (const FieldNode &Node : Chain) {
  272. if (Node.isSameRegion(FR))
  273. return true;
  274. }
  275. return false;
  276. }
  277. /// Prints every element except the last to `Out`. Since ImmutableLists store
  278. /// elements in reverse order, and have no reverse iterators, we use a
  279. /// recursive function to print the fieldchain correctly. The last element in
  280. /// the chain is to be printed by `FieldChainInfo::print`.
  281. static void printTail(llvm::raw_ostream &Out,
  282. const FieldChainInfo::FieldChain L);
  283. // FIXME: This function constructs an incorrect string in the following case:
  284. //
  285. // struct Base { int x; };
  286. // struct D1 : Base {}; struct D2 : Base {};
  287. //
  288. // struct MostDerived : D1, D2 {
  289. // MostDerived() {}
  290. // }
  291. //
  292. // A call to MostDerived::MostDerived() will cause two notes that say
  293. // "uninitialized field 'this->x'", but we can't refer to 'x' directly,
  294. // we need an explicit namespace resolution whether the uninit field was
  295. // 'D1::x' or 'D2::x'.
  296. void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const {
  297. if (Chain.isEmpty())
  298. return;
  299. const FieldNode &LastField = getHead();
  300. LastField.printNoteMsg(Out);
  301. Out << '\'';
  302. for (const FieldNode &Node : Chain)
  303. Node.printPrefix(Out);
  304. Out << "this->";
  305. printTail(Out, Chain.getTail());
  306. LastField.printNode(Out);
  307. Out << '\'';
  308. }
  309. static void printTail(llvm::raw_ostream &Out,
  310. const FieldChainInfo::FieldChain L) {
  311. if (L.isEmpty())
  312. return;
  313. printTail(Out, L.getTail());
  314. L.getHead().printNode(Out);
  315. L.getHead().printSeparator(Out);
  316. }
  317. //===----------------------------------------------------------------------===//
  318. // Utility functions.
  319. //===----------------------------------------------------------------------===//
  320. static const TypedValueRegion *
  321. getConstructedRegion(const CXXConstructorDecl *CtorDecl,
  322. CheckerContext &Context) {
  323. Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl,
  324. Context.getStackFrame());
  325. SVal ObjectV = Context.getState()->getSVal(ThisLoc);
  326. auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>();
  327. if (R && !R->getValueType()->getAsCXXRecordDecl())
  328. return nullptr;
  329. return R;
  330. }
  331. static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
  332. CheckerContext &Context) {
  333. const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context);
  334. if (!CurrRegion)
  335. return false;
  336. const LocationContext *LC = Context.getLocationContext();
  337. while ((LC = LC->getParent())) {
  338. // If \p Ctor was called by another constructor.
  339. const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
  340. if (!OtherCtor)
  341. continue;
  342. const TypedValueRegion *OtherRegion =
  343. getConstructedRegion(OtherCtor, Context);
  344. if (!OtherRegion)
  345. continue;
  346. // If the CurrRegion is a subregion of OtherRegion, it will be analyzed
  347. // during the analysis of OtherRegion.
  348. if (CurrRegion->isSubRegionOf(OtherRegion))
  349. return true;
  350. }
  351. return false;
  352. }
  353. static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
  354. llvm::Regex R(Pattern);
  355. for (const FieldDecl *FD : RD->fields()) {
  356. if (R.match(FD->getType().getAsString()))
  357. return true;
  358. if (R.match(FD->getName()))
  359. return true;
  360. }
  361. return false;
  362. }
  363. std::string clang::ento::getVariableName(const FieldDecl *Field) {
  364. // If Field is a captured lambda variable, Field->getName() will return with
  365. // an empty string. We can however acquire it's name from the lambda's
  366. // captures.
  367. const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
  368. if (CXXParent && CXXParent->isLambda()) {
  369. assert(CXXParent->captures_begin());
  370. auto It = CXXParent->captures_begin() + Field->getFieldIndex();
  371. if (It->capturesVariable())
  372. return llvm::Twine("/*captured variable*/" +
  373. It->getCapturedVar()->getName())
  374. .str();
  375. if (It->capturesThis())
  376. return "/*'this' capture*/";
  377. llvm_unreachable("No other capture type is expected!");
  378. }
  379. return Field->getName();
  380. }
  381. void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
  382. auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
  383. AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
  384. UninitObjCheckerOptions &ChOpts = Chk->Opts;
  385. ChOpts.IsPedantic =
  386. AnOpts.getCheckerBooleanOption("Pedantic", /*DefaultVal*/ false, Chk);
  387. ChOpts.ShouldConvertNotesToWarnings =
  388. AnOpts.getCheckerBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk);
  389. ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
  390. "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
  391. ChOpts.IgnoredRecordsWithFieldPattern =
  392. AnOpts.getCheckerStringOption("IgnoreRecordsWithField",
  393. /*DefaultVal*/ "", Chk);
  394. }