17330f729Sjoerg // UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This checker detects blocks that capture uninitialized values.
107330f729Sjoerg //
117330f729Sjoerg //===----------------------------------------------------------------------===//
127330f729Sjoerg
137330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
147330f729Sjoerg #include "clang/AST/Attr.h"
157330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
167330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
207330f729Sjoerg #include "llvm/ADT/SmallString.h"
217330f729Sjoerg #include "llvm/Support/raw_ostream.h"
227330f729Sjoerg
237330f729Sjoerg using namespace clang;
247330f729Sjoerg using namespace ento;
257330f729Sjoerg
267330f729Sjoerg namespace {
277330f729Sjoerg class UndefCapturedBlockVarChecker
287330f729Sjoerg : public Checker< check::PostStmt<BlockExpr> > {
297330f729Sjoerg mutable std::unique_ptr<BugType> BT;
307330f729Sjoerg
317330f729Sjoerg public:
327330f729Sjoerg void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
337330f729Sjoerg };
347330f729Sjoerg } // end anonymous namespace
357330f729Sjoerg
FindBlockDeclRefExpr(const Stmt * S,const VarDecl * VD)367330f729Sjoerg static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
377330f729Sjoerg const VarDecl *VD) {
387330f729Sjoerg if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S))
397330f729Sjoerg if (BR->getDecl() == VD)
407330f729Sjoerg return BR;
417330f729Sjoerg
427330f729Sjoerg for (const Stmt *Child : S->children())
437330f729Sjoerg if (Child)
447330f729Sjoerg if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD))
457330f729Sjoerg return BR;
467330f729Sjoerg
477330f729Sjoerg return nullptr;
487330f729Sjoerg }
497330f729Sjoerg
507330f729Sjoerg void
checkPostStmt(const BlockExpr * BE,CheckerContext & C) const517330f729Sjoerg UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
527330f729Sjoerg CheckerContext &C) const {
537330f729Sjoerg if (!BE->getBlockDecl()->hasCaptures())
547330f729Sjoerg return;
557330f729Sjoerg
567330f729Sjoerg ProgramStateRef state = C.getState();
577330f729Sjoerg auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
587330f729Sjoerg
597330f729Sjoerg BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
607330f729Sjoerg E = R->referenced_vars_end();
617330f729Sjoerg
627330f729Sjoerg for (; I != E; ++I) {
637330f729Sjoerg // This VarRegion is the region associated with the block; we need
647330f729Sjoerg // the one associated with the encompassing context.
657330f729Sjoerg const VarRegion *VR = I.getCapturedRegion();
667330f729Sjoerg const VarDecl *VD = VR->getDecl();
677330f729Sjoerg
687330f729Sjoerg if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
697330f729Sjoerg continue;
707330f729Sjoerg
717330f729Sjoerg // Get the VarRegion associated with VD in the local stack frame.
727330f729Sjoerg if (Optional<UndefinedVal> V =
737330f729Sjoerg state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
747330f729Sjoerg if (ExplodedNode *N = C.generateErrorNode()) {
757330f729Sjoerg if (!BT)
767330f729Sjoerg BT.reset(
777330f729Sjoerg new BuiltinBug(this, "uninitialized variable captured by block"));
787330f729Sjoerg
797330f729Sjoerg // Generate a bug report.
807330f729Sjoerg SmallString<128> buf;
817330f729Sjoerg llvm::raw_svector_ostream os(buf);
827330f729Sjoerg
837330f729Sjoerg os << "Variable '" << VD->getName()
847330f729Sjoerg << "' is uninitialized when captured by block";
857330f729Sjoerg
867330f729Sjoerg auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
877330f729Sjoerg if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
887330f729Sjoerg R->addRange(Ex->getSourceRange());
897330f729Sjoerg R->addVisitor(std::make_unique<FindLastStoreBRVisitor>(
907330f729Sjoerg *V, VR, /*EnableNullFPSuppression*/ false,
917330f729Sjoerg bugreporter::TrackingKind::Thorough));
927330f729Sjoerg R->disablePathPruning();
937330f729Sjoerg // need location of block
947330f729Sjoerg C.emitReport(std::move(R));
957330f729Sjoerg }
967330f729Sjoerg }
977330f729Sjoerg }
987330f729Sjoerg }
997330f729Sjoerg
registerUndefCapturedBlockVarChecker(CheckerManager & mgr)1007330f729Sjoerg void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
1017330f729Sjoerg mgr.registerChecker<UndefCapturedBlockVarChecker>();
1027330f729Sjoerg }
1037330f729Sjoerg
shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager & mgr)104*e038c9c4Sjoerg bool ento::shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager &mgr) {
1057330f729Sjoerg return true;
1067330f729Sjoerg }
107