llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
Kristof Umann 3f7c66d551 [analyzer][NFC] Prepare visitors for different tracking kinds
When we're tracking a variable that is responsible for a null pointer
dereference or some other sinister programming error, we of course would like to
gather as much information why we think that the variable has that specific
value as possible. However, the newly introduced condition tracking shows that
tracking all values this thoroughly could easily cause an intolerable growth in
the bug report's length.

There are a variety of heuristics we discussed on the mailing list[1] to combat
this, all of them requiring to differentiate in between tracking a "regular
value" and a "condition".

This patch introduces the new `bugreporter::TrackingKind` enum, adds it to
several visitors as a non-optional argument, and moves some functions around to
make the code a little more coherent.

[1] http://lists.llvm.org/pipermail/cfe-dev/2019-June/062613.html

Differential Revision: https://reviews.llvm.org/D64270

llvm-svn: 368777
2019-08-14 00:48:57 +00:00

107 lines
3.6 KiB
C++

// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This checker detects blocks that capture uninitialized values.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/Attr.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
class UndefCapturedBlockVarChecker
: public Checker< check::PostStmt<BlockExpr> > {
mutable std::unique_ptr<BugType> BT;
public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
};
} // end anonymous namespace
static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
const VarDecl *VD) {
if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S))
if (BR->getDecl() == VD)
return BR;
for (const Stmt *Child : S->children())
if (Child)
if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD))
return BR;
return nullptr;
}
void
UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
CheckerContext &C) const {
if (!BE->getBlockDecl()->hasCaptures())
return;
ProgramStateRef state = C.getState();
auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
E = R->referenced_vars_end();
for (; I != E; ++I) {
// This VarRegion is the region associated with the block; we need
// the one associated with the encompassing context.
const VarRegion *VR = I.getCapturedRegion();
const VarDecl *VD = VR->getDecl();
if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
continue;
// Get the VarRegion associated with VD in the local stack frame.
if (Optional<UndefinedVal> V =
state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
if (ExplodedNode *N = C.generateErrorNode()) {
if (!BT)
BT.reset(
new BuiltinBug(this, "uninitialized variable captured by block"));
// Generate a bug report.
SmallString<128> buf;
llvm::raw_svector_ostream os(buf);
os << "Variable '" << VD->getName()
<< "' is uninitialized when captured by block";
auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
R->addRange(Ex->getSourceRange());
R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
*V, VR, /*EnableNullFPSuppression*/ false,
bugreporter::TrackingKind::Thorough));
R->disablePathPruning();
// need location of block
C.emitReport(std::move(R));
}
}
}
}
void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefCapturedBlockVarChecker>();
}
bool ento::shouldRegisterUndefCapturedBlockVarChecker(const LangOptions &LO) {
return true;
}