1218887Sdim// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=// 2218887Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6218887Sdim// 7218887Sdim//===----------------------------------------------------------------------===// 8218887Sdim// 9218887Sdim// This checker detects blocks that capture uninitialized values. 10218887Sdim// 11218887Sdim//===----------------------------------------------------------------------===// 12218887Sdim 13344779Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14249423Sdim#include "clang/AST/Attr.h" 15249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 17221345Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 18221345Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 20234353Sdim#include "llvm/ADT/SmallString.h" 21218887Sdim#include "llvm/Support/raw_ostream.h" 22218887Sdim 23218887Sdimusing namespace clang; 24218887Sdimusing namespace ento; 25218887Sdim 26218887Sdimnamespace { 27218887Sdimclass UndefCapturedBlockVarChecker 28221345Sdim : public Checker< check::PostStmt<BlockExpr> > { 29276479Sdim mutable std::unique_ptr<BugType> BT; 30218887Sdim 31218887Sdimpublic: 32221345Sdim void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; 33218887Sdim}; 34218887Sdim} // end anonymous namespace 35218887Sdim 36234353Sdimstatic const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, 37234353Sdim const VarDecl *VD) { 38234353Sdim if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S)) 39218887Sdim if (BR->getDecl() == VD) 40218887Sdim return BR; 41218887Sdim 42288943Sdim for (const Stmt *Child : S->children()) 43288943Sdim if (Child) 44288943Sdim if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD)) 45218887Sdim return BR; 46218887Sdim 47276479Sdim return nullptr; 48218887Sdim} 49218887Sdim 50218887Sdimvoid 51221345SdimUndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, 52221345Sdim CheckerContext &C) const { 53218887Sdim if (!BE->getBlockDecl()->hasCaptures()) 54218887Sdim return; 55218887Sdim 56234353Sdim ProgramStateRef state = C.getState(); 57341825Sdim auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); 58218887Sdim 59218887Sdim BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), 60218887Sdim E = R->referenced_vars_end(); 61218887Sdim 62218887Sdim for (; I != E; ++I) { 63218887Sdim // This VarRegion is the region associated with the block; we need 64218887Sdim // the one associated with the encompassing context. 65249423Sdim const VarRegion *VR = I.getCapturedRegion(); 66218887Sdim const VarDecl *VD = VR->getDecl(); 67218887Sdim 68276479Sdim if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) 69218887Sdim continue; 70218887Sdim 71218887Sdim // Get the VarRegion associated with VD in the local stack frame. 72249423Sdim if (Optional<UndefinedVal> V = 73249423Sdim state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { 74296417Sdim if (ExplodedNode *N = C.generateErrorNode()) { 75218887Sdim if (!BT) 76276479Sdim BT.reset( 77276479Sdim new BuiltinBug(this, "uninitialized variable captured by block")); 78218887Sdim 79218887Sdim // Generate a bug report. 80234353Sdim SmallString<128> buf; 81218887Sdim llvm::raw_svector_ostream os(buf); 82218887Sdim 83296417Sdim os << "Variable '" << VD->getName() 84218887Sdim << "' is uninitialized when captured by block"; 85218887Sdim 86360784Sdim auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); 87218887Sdim if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) 88218887Sdim R->addRange(Ex->getSourceRange()); 89360784Sdim R->addVisitor(std::make_unique<FindLastStoreBRVisitor>( 90360784Sdim *V, VR, /*EnableNullFPSuppression*/ false, 91360784Sdim bugreporter::TrackingKind::Thorough)); 92239462Sdim R->disablePathPruning(); 93218887Sdim // need location of block 94288943Sdim C.emitReport(std::move(R)); 95218887Sdim } 96249423Sdim } 97218887Sdim } 98218887Sdim} 99221345Sdim 100221345Sdimvoid ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) { 101221345Sdim mgr.registerChecker<UndefCapturedBlockVarChecker>(); 102221345Sdim} 103353358Sdim 104353358Sdimbool ento::shouldRegisterUndefCapturedBlockVarChecker(const LangOptions &LO) { 105353358Sdim return true; 106353358Sdim} 107