xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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