101 lines · cpp
1// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This checker detects blocks that capture uninitialized values.10//11//===----------------------------------------------------------------------===//12 13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"14#include "clang/AST/Attr.h"15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include "clang/StaticAnalyzer/Core/CheckerManager.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"20#include "llvm/ADT/SmallString.h"21#include "llvm/Support/raw_ostream.h"22#include <optional>23 24using namespace clang;25using namespace ento;26 27namespace {28class UndefCapturedBlockVarChecker29 : public Checker< check::PostStmt<BlockExpr> > {30 const BugType BT{this, "uninitialized variable captured by block"};31 32public:33 void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;34};35} // end anonymous namespace36 37static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,38 const VarDecl *VD) {39 if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S))40 if (BR->getDecl() == VD)41 return BR;42 43 for (const Stmt *Child : S->children())44 if (Child)45 if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD))46 return BR;47 48 return nullptr;49}50 51void52UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,53 CheckerContext &C) const {54 if (!BE->getBlockDecl()->hasCaptures())55 return;56 57 ProgramStateRef state = C.getState();58 auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());59 60 for (auto Var : R->referenced_vars()) {61 // This VarRegion is the region associated with the block; we need62 // the one associated with the encompassing context.63 const VarRegion *VR = Var.getCapturedRegion();64 const VarDecl *VD = VR->getDecl();65 66 if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())67 continue;68 69 // Get the VarRegion associated with VD in the local stack frame.70 if (std::optional<UndefinedVal> V =71 state->getSVal(Var.getOriginalRegion()).getAs<UndefinedVal>()) {72 if (ExplodedNode *N = C.generateErrorNode()) {73 // Generate a bug report.74 SmallString<128> buf;75 llvm::raw_svector_ostream os(buf);76 77 os << "Variable '" << VD->getName()78 << "' is uninitialized when captured by block";79 80 auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);81 if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))82 R->addRange(Ex->getSourceRange());83 bugreporter::trackStoredValue(*V, VR, *R,84 {bugreporter::TrackingKind::Thorough,85 /*EnableNullFPSuppression*/ false});86 R->disablePathPruning();87 // need location of block88 C.emitReport(std::move(R));89 }90 }91 }92}93 94void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {95 mgr.registerChecker<UndefCapturedBlockVarChecker>();96}97 98bool ento::shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager &mgr) {99 return true;100}101