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
107 lines
3.6 KiB
C++
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;
|
|
}
|