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