2009-11-09 21:23:31 +08:00
|
|
|
//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2009-11-09 21:23:31 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This files defines PointerArithChecker, a builtin checker that checks for
|
|
|
|
// pointer arithmetic on locations other than array elements.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2016-02-23 20:34:39 +08:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
|
|
|
#include "clang/AST/ExprCXX.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-02-18 05:39:17 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2011-02-23 09:05:36 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2009-11-09 21:23:31 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2009-11-09 21:23:31 +08:00
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
namespace {
|
|
|
|
enum class AllocKind {
|
|
|
|
SingleObject,
|
|
|
|
Array,
|
|
|
|
Unknown,
|
|
|
|
Reinterpreted // Single object interpreted as an array.
|
|
|
|
};
|
|
|
|
} // end namespace
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
template <> struct FoldingSetTrait<AllocKind> {
|
|
|
|
static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
|
|
|
|
ID.AddInteger(static_cast<int>(X));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end namespace llvm
|
|
|
|
|
2009-11-09 21:23:31 +08:00
|
|
|
namespace {
|
2015-09-08 11:50:52 +08:00
|
|
|
class PointerArithChecker
|
2016-02-23 20:34:39 +08:00
|
|
|
: public Checker<
|
|
|
|
check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
|
|
|
|
check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
|
|
|
|
check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
|
|
|
|
check::PostStmt<CallExpr>, check::DeadSymbols> {
|
|
|
|
AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
|
|
|
|
const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
|
|
|
|
AllocKind &AKind, CheckerContext &C) const;
|
|
|
|
const MemRegion *getPointedRegion(const MemRegion *Region,
|
|
|
|
CheckerContext &C) const;
|
|
|
|
void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
|
|
|
|
bool PointedNeeded = false) const;
|
|
|
|
void initAllocIdentifiers(ASTContext &C) const;
|
|
|
|
|
|
|
|
mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
|
|
|
|
mutable std::unique_ptr<BuiltinBug> BT_polyArray;
|
|
|
|
mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
|
2011-02-23 09:05:36 +08:00
|
|
|
|
2009-11-09 21:23:31 +08:00
|
|
|
public:
|
2016-02-23 20:34:39 +08:00
|
|
|
void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
|
|
|
|
void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
|
|
|
|
void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
|
|
|
|
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
|
|
|
|
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
|
|
|
|
void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
|
|
|
|
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
|
|
|
|
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
2009-11-09 21:23:31 +08:00
|
|
|
};
|
2016-02-23 20:34:39 +08:00
|
|
|
} // end namespace
|
|
|
|
|
|
|
|
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
|
|
|
|
|
|
|
|
void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
// TODO: intentional leak. Some information is garbage collected too early,
|
|
|
|
// see http://reviews.llvm.org/D14203 for further information.
|
|
|
|
/*ProgramStateRef State = C.getState();
|
|
|
|
RegionStateTy RegionStates = State->get<RegionState>();
|
|
|
|
for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
if (!SR.isLiveRegion(I->first))
|
|
|
|
State = State->remove<RegionState>(I->first);
|
|
|
|
}
|
|
|
|
C.addTransition(State);*/
|
2009-11-09 21:23:31 +08:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
|
|
|
|
const FunctionDecl *FD) const {
|
|
|
|
// This checker try not to assume anything about placement and overloaded
|
|
|
|
// new to avoid false positives.
|
|
|
|
if (isa<CXXMethodDecl>(FD))
|
|
|
|
return AllocKind::Unknown;
|
|
|
|
if (FD->getNumParams() != 1 || FD->isVariadic())
|
|
|
|
return AllocKind::Unknown;
|
|
|
|
if (NE->isArray())
|
|
|
|
return AllocKind::Array;
|
|
|
|
|
|
|
|
return AllocKind::SingleObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
const MemRegion *
|
|
|
|
PointerArithChecker::getPointedRegion(const MemRegion *Region,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
assert(Region);
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
SVal S = State->getSVal(Region);
|
|
|
|
return S.getAsRegion();
|
|
|
|
}
|
2009-11-09 21:23:31 +08:00
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
/// Checks whether a region is the part of an array.
|
Misc typos fixes in ./lib folder
Summary: Found via `codespell -q 3 -I ../clang-whitelist.txt -L uint,importd,crasher,gonna,cant,ue,ons,orign,ned`
Reviewers: teemperor
Reviewed By: teemperor
Subscribers: teemperor, jholewinski, jvesely, nhaehnle, whisperity, jfb, cfe-commits
Differential Revision: https://reviews.llvm.org/D55475
llvm-svn: 348755
2018-12-10 20:37:46 +08:00
|
|
|
/// In case there is a derived to base cast above the array element, the
|
2016-02-23 20:34:39 +08:00
|
|
|
/// Polymorphic output value is set to true. AKind output value is set to the
|
|
|
|
/// allocation kind of the inspected region.
|
|
|
|
const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
|
|
|
|
bool &Polymorphic,
|
|
|
|
AllocKind &AKind,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
assert(Region);
|
2019-08-29 02:44:38 +08:00
|
|
|
while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
|
|
|
|
Region = BaseRegion->getSuperRegion();
|
2016-02-23 20:34:39 +08:00
|
|
|
Polymorphic = true;
|
|
|
|
}
|
2019-08-29 02:44:38 +08:00
|
|
|
if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
|
|
|
|
Region = ElemRegion->getSuperRegion();
|
2016-02-23 20:34:39 +08:00
|
|
|
}
|
2009-11-09 21:23:31 +08:00
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
if (const AllocKind *Kind = State->get<RegionState>(Region)) {
|
|
|
|
AKind = *Kind;
|
|
|
|
if (*Kind == AllocKind::Array)
|
|
|
|
return Region;
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
// When the region is symbolic and we do not have any information about it,
|
|
|
|
// assume that this is an array to avoid false positives.
|
2019-08-29 02:44:38 +08:00
|
|
|
if (isa<SymbolicRegion>(Region))
|
2016-02-23 20:34:39 +08:00
|
|
|
return Region;
|
2009-11-09 21:23:31 +08:00
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
// No AllocKind stored and not symbolic, assume that it points to a single
|
|
|
|
// object.
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
|
|
|
|
CheckerContext &C,
|
|
|
|
bool PointedNeeded) const {
|
|
|
|
SourceRange SR = E->getSourceRange();
|
|
|
|
if (SR.isInvalid())
|
2009-11-09 21:23:31 +08:00
|
|
|
return;
|
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
ProgramStateRef State = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
const MemRegion *Region = C.getSVal(E).getAsRegion();
|
2016-02-23 20:34:39 +08:00
|
|
|
if (!Region)
|
|
|
|
return;
|
|
|
|
if (PointedNeeded)
|
|
|
|
Region = getPointedRegion(Region, C);
|
|
|
|
if (!Region)
|
|
|
|
return;
|
2009-11-09 21:23:31 +08:00
|
|
|
|
2016-02-23 20:34:39 +08:00
|
|
|
bool IsPolymorphic = false;
|
|
|
|
AllocKind Kind = AllocKind::Unknown;
|
|
|
|
if (const MemRegion *ArrayRegion =
|
|
|
|
getArrayRegion(Region, IsPolymorphic, Kind, C)) {
|
|
|
|
if (!IsPolymorphic)
|
|
|
|
return;
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
|
2016-02-23 20:34:39 +08:00
|
|
|
if (!BT_polyArray)
|
|
|
|
BT_polyArray.reset(new BuiltinBug(
|
|
|
|
this, "Dangerous pointer arithmetic",
|
|
|
|
"Pointer arithmetic on a pointer to base class is dangerous "
|
|
|
|
"because derived and base class may have different size."));
|
2019-09-10 04:34:40 +08:00
|
|
|
auto R = std::make_unique<PathSensitiveBugReport>(
|
|
|
|
*BT_polyArray, BT_polyArray->getDescription(), N);
|
2016-02-23 20:34:39 +08:00
|
|
|
R->addRange(E->getSourceRange());
|
|
|
|
R->markInteresting(ArrayRegion);
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(R));
|
2009-11-09 21:23:31 +08:00
|
|
|
}
|
2016-02-23 20:34:39 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Kind == AllocKind::Reinterpreted)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We might not have enough information about symbolic regions.
|
|
|
|
if (Kind != AllocKind::SingleObject &&
|
|
|
|
Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
|
|
|
|
if (!BT_pointerArith)
|
|
|
|
BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
|
|
|
|
"Pointer arithmetic on non-array "
|
|
|
|
"variables relies on memory layout, "
|
|
|
|
"which is dangerous."));
|
2019-09-10 04:34:40 +08:00
|
|
|
auto R = std::make_unique<PathSensitiveBugReport>(
|
|
|
|
*BT_pointerArith, BT_pointerArith->getDescription(), N);
|
2016-02-23 20:34:39 +08:00
|
|
|
R->addRange(SR);
|
|
|
|
R->markInteresting(Region);
|
|
|
|
C.emitReport(std::move(R));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
|
|
|
|
if (!AllocFunctions.empty())
|
|
|
|
return;
|
|
|
|
AllocFunctions.insert(&C.Idents.get("alloca"));
|
|
|
|
AllocFunctions.insert(&C.Idents.get("malloc"));
|
|
|
|
AllocFunctions.insert(&C.Idents.get("realloc"));
|
|
|
|
AllocFunctions.insert(&C.Idents.get("calloc"));
|
|
|
|
AllocFunctions.insert(&C.Idents.get("valloc"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPostStmt(const CallExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
|
|
|
if (!FD)
|
|
|
|
return;
|
|
|
|
IdentifierInfo *FunI = FD->getIdentifier();
|
|
|
|
initAllocIdentifiers(C.getASTContext());
|
|
|
|
if (AllocFunctions.count(FunI) == 0)
|
|
|
|
return;
|
|
|
|
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal SV = C.getSVal(CE);
|
2016-02-23 20:34:39 +08:00
|
|
|
const MemRegion *Region = SV.getAsRegion();
|
|
|
|
if (!Region)
|
|
|
|
return;
|
|
|
|
// Assume that C allocation functions allocate arrays to avoid false
|
|
|
|
// positives.
|
|
|
|
// TODO: Add heuristics to distinguish alloc calls that allocates single
|
|
|
|
// objecs.
|
|
|
|
State = State->set<RegionState>(Region, AllocKind::Array);
|
|
|
|
C.addTransition(State);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
const FunctionDecl *FD = NE->getOperatorNew();
|
|
|
|
if (!FD)
|
|
|
|
return;
|
|
|
|
|
|
|
|
AllocKind Kind = getKindOfNewOp(NE, FD);
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal AllocedVal = C.getSVal(NE);
|
2016-02-23 20:34:39 +08:00
|
|
|
const MemRegion *Region = AllocedVal.getAsRegion();
|
|
|
|
if (!Region)
|
|
|
|
return;
|
|
|
|
State = State->set<RegionState>(Region, Kind);
|
|
|
|
C.addTransition(State);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPostStmt(const CastExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
if (CE->getCastKind() != CastKind::CK_BitCast)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const Expr *CastedExpr = CE->getSubExpr();
|
|
|
|
ProgramStateRef State = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal CastedVal = C.getSVal(CastedExpr);
|
2016-02-23 20:34:39 +08:00
|
|
|
|
|
|
|
const MemRegion *Region = CastedVal.getAsRegion();
|
|
|
|
if (!Region)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Suppress reinterpret casted hits.
|
|
|
|
State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
|
|
|
|
C.addTransition(State);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPreStmt(const CastExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const Expr *CastedExpr = CE->getSubExpr();
|
|
|
|
ProgramStateRef State = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal CastedVal = C.getSVal(CastedExpr);
|
2016-02-23 20:34:39 +08:00
|
|
|
|
|
|
|
const MemRegion *Region = CastedVal.getAsRegion();
|
|
|
|
if (!Region)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (const AllocKind *Kind = State->get<RegionState>(Region)) {
|
|
|
|
if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
State = State->set<RegionState>(Region, AllocKind::Array);
|
|
|
|
C.addTransition(State);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
|
|
|
|
return;
|
|
|
|
reportPointerArithMisuse(UOp->getSubExpr(), C, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
|
|
|
|
CheckerContext &C) const {
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal Idx = C.getSVal(SubsExpr->getIdx());
|
2016-02-23 20:34:39 +08:00
|
|
|
|
|
|
|
// Indexing with 0 is OK.
|
|
|
|
if (Idx.isZeroConstant())
|
|
|
|
return;
|
2018-03-08 06:20:39 +08:00
|
|
|
|
|
|
|
// Indexing vector-type expressions is also OK.
|
|
|
|
if (SubsExpr->getBase()->getType()->isVectorType())
|
|
|
|
return;
|
2016-02-23 20:34:39 +08:00
|
|
|
reportPointerArithMisuse(SubsExpr->getBase(), C);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
BinaryOperatorKind OpKind = BOp->getOpcode();
|
|
|
|
if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const Expr *Lhs = BOp->getLHS();
|
|
|
|
const Expr *Rhs = BOp->getRHS();
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
|
|
|
if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal RHSVal = C.getSVal(Rhs);
|
2016-02-23 20:34:39 +08:00
|
|
|
if (State->isNull(RHSVal).isConstrainedTrue())
|
|
|
|
return;
|
|
|
|
reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
|
|
|
|
}
|
|
|
|
// The int += ptr; case is not valid C++.
|
|
|
|
if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal LHSVal = C.getSVal(Lhs);
|
2016-02-23 20:34:39 +08:00
|
|
|
if (State->isNull(LHSVal).isConstrainedTrue())
|
|
|
|
return;
|
|
|
|
reportPointerArithMisuse(Rhs, C);
|
2009-11-09 21:23:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-18 05:39:17 +08:00
|
|
|
void ento::registerPointerArithChecker(CheckerManager &mgr) {
|
2011-02-23 09:05:36 +08:00
|
|
|
mgr.registerChecker<PointerArithChecker>();
|
2011-02-18 05:39:17 +08:00
|
|
|
}
|
2019-01-26 22:23:08 +08:00
|
|
|
|
2020-03-27 21:29:31 +08:00
|
|
|
bool ento::shouldRegisterPointerArithChecker(const CheckerManager &mgr) {
|
2019-01-26 22:23:08 +08:00
|
|
|
return true;
|
|
|
|
}
|