10b57cec5SDimitry Andric // UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This checker detects blocks that capture uninitialized values.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
140b57cec5SDimitry Andric #include "clang/AST/Attr.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
200b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
220b57cec5SDimitry Andric
230b57cec5SDimitry Andric using namespace clang;
240b57cec5SDimitry Andric using namespace ento;
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric namespace {
270b57cec5SDimitry Andric class UndefCapturedBlockVarChecker
280b57cec5SDimitry Andric : public Checker< check::PostStmt<BlockExpr> > {
290b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT;
300b57cec5SDimitry Andric
310b57cec5SDimitry Andric public:
320b57cec5SDimitry Andric void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
330b57cec5SDimitry Andric };
340b57cec5SDimitry Andric } // end anonymous namespace
350b57cec5SDimitry Andric
FindBlockDeclRefExpr(const Stmt * S,const VarDecl * VD)360b57cec5SDimitry Andric static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
370b57cec5SDimitry Andric const VarDecl *VD) {
380b57cec5SDimitry Andric if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S))
390b57cec5SDimitry Andric if (BR->getDecl() == VD)
400b57cec5SDimitry Andric return BR;
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric for (const Stmt *Child : S->children())
430b57cec5SDimitry Andric if (Child)
440b57cec5SDimitry Andric if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD))
450b57cec5SDimitry Andric return BR;
460b57cec5SDimitry Andric
470b57cec5SDimitry Andric return nullptr;
480b57cec5SDimitry Andric }
490b57cec5SDimitry Andric
500b57cec5SDimitry Andric void
checkPostStmt(const BlockExpr * BE,CheckerContext & C) const510b57cec5SDimitry Andric UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
520b57cec5SDimitry Andric CheckerContext &C) const {
530b57cec5SDimitry Andric if (!BE->getBlockDecl()->hasCaptures())
540b57cec5SDimitry Andric return;
550b57cec5SDimitry Andric
560b57cec5SDimitry Andric ProgramStateRef state = C.getState();
570b57cec5SDimitry Andric auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
580b57cec5SDimitry Andric
590b57cec5SDimitry Andric BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
600b57cec5SDimitry Andric E = R->referenced_vars_end();
610b57cec5SDimitry Andric
620b57cec5SDimitry Andric for (; I != E; ++I) {
630b57cec5SDimitry Andric // This VarRegion is the region associated with the block; we need
640b57cec5SDimitry Andric // the one associated with the encompassing context.
650b57cec5SDimitry Andric const VarRegion *VR = I.getCapturedRegion();
660b57cec5SDimitry Andric const VarDecl *VD = VR->getDecl();
670b57cec5SDimitry Andric
680b57cec5SDimitry Andric if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
690b57cec5SDimitry Andric continue;
700b57cec5SDimitry Andric
710b57cec5SDimitry Andric // Get the VarRegion associated with VD in the local stack frame.
720b57cec5SDimitry Andric if (Optional<UndefinedVal> V =
730b57cec5SDimitry Andric state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
740b57cec5SDimitry Andric if (ExplodedNode *N = C.generateErrorNode()) {
750b57cec5SDimitry Andric if (!BT)
760b57cec5SDimitry Andric BT.reset(
770b57cec5SDimitry Andric new BuiltinBug(this, "uninitialized variable captured by block"));
780b57cec5SDimitry Andric
790b57cec5SDimitry Andric // Generate a bug report.
800b57cec5SDimitry Andric SmallString<128> buf;
810b57cec5SDimitry Andric llvm::raw_svector_ostream os(buf);
820b57cec5SDimitry Andric
830b57cec5SDimitry Andric os << "Variable '" << VD->getName()
840b57cec5SDimitry Andric << "' is uninitialized when captured by block";
850b57cec5SDimitry Andric
86a7dea167SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
870b57cec5SDimitry Andric if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
880b57cec5SDimitry Andric R->addRange(Ex->getSourceRange());
89*5f7ddb14SDimitry Andric bugreporter::trackStoredValue(*V, VR, *R,
90*5f7ddb14SDimitry Andric {bugreporter::TrackingKind::Thorough,
91*5f7ddb14SDimitry Andric /*EnableNullFPSuppression*/ false});
920b57cec5SDimitry Andric R->disablePathPruning();
930b57cec5SDimitry Andric // need location of block
940b57cec5SDimitry Andric C.emitReport(std::move(R));
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric }
990b57cec5SDimitry Andric
registerUndefCapturedBlockVarChecker(CheckerManager & mgr)1000b57cec5SDimitry Andric void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
1010b57cec5SDimitry Andric mgr.registerChecker<UndefCapturedBlockVarChecker>();
1020b57cec5SDimitry Andric }
1030b57cec5SDimitry Andric
shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager & mgr)1045ffd83dbSDimitry Andric bool ento::shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager &mgr) {
1050b57cec5SDimitry Andric return true;
1060b57cec5SDimitry Andric }
107