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