12754fe60SDimitry Andric // UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=//
22754fe60SDimitry Andric //
32754fe60SDimitry Andric //                     The LLVM Compiler Infrastructure
42754fe60SDimitry Andric //
52754fe60SDimitry Andric // This file is distributed under the University of Illinois Open Source
62754fe60SDimitry Andric // License. See LICENSE.TXT for details.
72754fe60SDimitry Andric //
82754fe60SDimitry Andric //===----------------------------------------------------------------------===//
92754fe60SDimitry Andric //
102754fe60SDimitry Andric // This checker detects blocks that capture uninitialized values.
112754fe60SDimitry Andric //
122754fe60SDimitry Andric //===----------------------------------------------------------------------===//
132754fe60SDimitry Andric 
14*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15139f7f9bSDimitry Andric #include "clang/AST/Attr.h"
16139f7f9bSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
173b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
183b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
193b0f4066SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
202754fe60SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21dff0c46cSDimitry Andric #include "llvm/ADT/SmallString.h"
222754fe60SDimitry Andric #include "llvm/Support/raw_ostream.h"
232754fe60SDimitry Andric 
242754fe60SDimitry Andric using namespace clang;
252754fe60SDimitry Andric using namespace ento;
262754fe60SDimitry Andric 
272754fe60SDimitry Andric namespace {
282754fe60SDimitry Andric class UndefCapturedBlockVarChecker
293b0f4066SDimitry Andric   : public Checker< check::PostStmt<BlockExpr> > {
3059d1ed5bSDimitry Andric   mutable std::unique_ptr<BugType> BT;
312754fe60SDimitry Andric 
322754fe60SDimitry Andric public:
333b0f4066SDimitry Andric   void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
342754fe60SDimitry Andric };
352754fe60SDimitry Andric } // end anonymous namespace
362754fe60SDimitry Andric 
FindBlockDeclRefExpr(const Stmt * S,const VarDecl * VD)37dff0c46cSDimitry Andric static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
382754fe60SDimitry Andric                                                const VarDecl *VD) {
39dff0c46cSDimitry Andric   if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S))
402754fe60SDimitry Andric     if (BR->getDecl() == VD)
412754fe60SDimitry Andric       return BR;
422754fe60SDimitry Andric 
433dac3a9bSDimitry Andric   for (const Stmt *Child : S->children())
443dac3a9bSDimitry Andric     if (Child)
453dac3a9bSDimitry Andric       if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD))
462754fe60SDimitry Andric         return BR;
472754fe60SDimitry Andric 
4859d1ed5bSDimitry Andric   return nullptr;
492754fe60SDimitry Andric }
502754fe60SDimitry Andric 
512754fe60SDimitry Andric void
checkPostStmt(const BlockExpr * BE,CheckerContext & C) const523b0f4066SDimitry Andric UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
533b0f4066SDimitry Andric                                             CheckerContext &C) const {
542754fe60SDimitry Andric   if (!BE->getBlockDecl()->hasCaptures())
552754fe60SDimitry Andric     return;
562754fe60SDimitry Andric 
57dff0c46cSDimitry Andric   ProgramStateRef state = C.getState();
584ba319b5SDimitry Andric   auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
592754fe60SDimitry Andric 
602754fe60SDimitry Andric   BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
612754fe60SDimitry Andric                                             E = R->referenced_vars_end();
622754fe60SDimitry Andric 
632754fe60SDimitry Andric   for (; I != E; ++I) {
642754fe60SDimitry Andric     // This VarRegion is the region associated with the block; we need
652754fe60SDimitry Andric     // the one associated with the encompassing context.
66139f7f9bSDimitry Andric     const VarRegion *VR = I.getCapturedRegion();
672754fe60SDimitry Andric     const VarDecl *VD = VR->getDecl();
682754fe60SDimitry Andric 
6959d1ed5bSDimitry Andric     if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
702754fe60SDimitry Andric       continue;
712754fe60SDimitry Andric 
722754fe60SDimitry Andric     // Get the VarRegion associated with VD in the local stack frame.
73139f7f9bSDimitry Andric     if (Optional<UndefinedVal> V =
74139f7f9bSDimitry Andric           state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
750623d748SDimitry Andric       if (ExplodedNode *N = C.generateErrorNode()) {
762754fe60SDimitry Andric         if (!BT)
7759d1ed5bSDimitry Andric           BT.reset(
7859d1ed5bSDimitry Andric               new BuiltinBug(this, "uninitialized variable captured by block"));
792754fe60SDimitry Andric 
802754fe60SDimitry Andric         // Generate a bug report.
81dff0c46cSDimitry Andric         SmallString<128> buf;
822754fe60SDimitry Andric         llvm::raw_svector_ostream os(buf);
832754fe60SDimitry Andric 
842754fe60SDimitry Andric         os << "Variable '" << VD->getName()
852754fe60SDimitry Andric            << "' is uninitialized when captured by block";
862754fe60SDimitry Andric 
873dac3a9bSDimitry Andric         auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
882754fe60SDimitry Andric         if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
892754fe60SDimitry Andric           R->addRange(Ex->getSourceRange());
9039d628a0SDimitry Andric         R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
9139d628a0SDimitry Andric             *V, VR, /*EnableNullFPSuppression*/ false));
927ae0e2c9SDimitry Andric         R->disablePathPruning();
932754fe60SDimitry Andric         // need location of block
943dac3a9bSDimitry Andric         C.emitReport(std::move(R));
952754fe60SDimitry Andric       }
962754fe60SDimitry Andric     }
972754fe60SDimitry Andric   }
98139f7f9bSDimitry Andric }
993b0f4066SDimitry Andric 
registerUndefCapturedBlockVarChecker(CheckerManager & mgr)1003b0f4066SDimitry Andric void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
1013b0f4066SDimitry Andric   mgr.registerChecker<UndefCapturedBlockVarChecker>();
1023b0f4066SDimitry Andric }
103