10b57cec5SDimitry Andric //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This files defines PointerArithChecker, a builtin checker that checks for
100b57cec5SDimitry Andric // pointer arithmetic on locations other than array elements.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h"
150b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h"
165f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
215f757f3fSDimitry Andric #include "llvm/ADT/StringRef.h"
220b57cec5SDimitry Andric
230b57cec5SDimitry Andric using namespace clang;
240b57cec5SDimitry Andric using namespace ento;
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric namespace {
270b57cec5SDimitry Andric enum class AllocKind {
280b57cec5SDimitry Andric SingleObject,
290b57cec5SDimitry Andric Array,
300b57cec5SDimitry Andric Unknown,
310b57cec5SDimitry Andric Reinterpreted // Single object interpreted as an array.
320b57cec5SDimitry Andric };
330b57cec5SDimitry Andric } // end namespace
340b57cec5SDimitry Andric
350b57cec5SDimitry Andric namespace llvm {
360b57cec5SDimitry Andric template <> struct FoldingSetTrait<AllocKind> {
Profilellvm::FoldingSetTrait370b57cec5SDimitry Andric static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
380b57cec5SDimitry Andric ID.AddInteger(static_cast<int>(X));
390b57cec5SDimitry Andric }
400b57cec5SDimitry Andric };
410b57cec5SDimitry Andric } // end namespace llvm
420b57cec5SDimitry Andric
430b57cec5SDimitry Andric namespace {
440b57cec5SDimitry Andric class PointerArithChecker
450b57cec5SDimitry Andric : public Checker<
460b57cec5SDimitry Andric check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
470b57cec5SDimitry Andric check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
480b57cec5SDimitry Andric check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
490b57cec5SDimitry Andric check::PostStmt<CallExpr>, check::DeadSymbols> {
500b57cec5SDimitry Andric AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
510b57cec5SDimitry Andric const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
520b57cec5SDimitry Andric AllocKind &AKind, CheckerContext &C) const;
530b57cec5SDimitry Andric const MemRegion *getPointedRegion(const MemRegion *Region,
540b57cec5SDimitry Andric CheckerContext &C) const;
550b57cec5SDimitry Andric void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
560b57cec5SDimitry Andric bool PointedNeeded = false) const;
570b57cec5SDimitry Andric void initAllocIdentifiers(ASTContext &C) const;
580b57cec5SDimitry Andric
59647cbc5dSDimitry Andric const BugType BT_pointerArith{this, "Dangerous pointer arithmetic"};
60647cbc5dSDimitry Andric const BugType BT_polyArray{this, "Dangerous pointer arithmetic"};
610b57cec5SDimitry Andric mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
620b57cec5SDimitry Andric
630b57cec5SDimitry Andric public:
640b57cec5SDimitry Andric void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
650b57cec5SDimitry Andric void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
660b57cec5SDimitry Andric void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
670b57cec5SDimitry Andric void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
680b57cec5SDimitry Andric void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
690b57cec5SDimitry Andric void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
700b57cec5SDimitry Andric void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
710b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
720b57cec5SDimitry Andric };
730b57cec5SDimitry Andric } // end namespace
740b57cec5SDimitry Andric
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState,const MemRegion *,AllocKind)750b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
760b57cec5SDimitry Andric
770b57cec5SDimitry Andric void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
780b57cec5SDimitry Andric CheckerContext &C) const {
790b57cec5SDimitry Andric // TODO: intentional leak. Some information is garbage collected too early,
800b57cec5SDimitry Andric // see http://reviews.llvm.org/D14203 for further information.
810b57cec5SDimitry Andric /*ProgramStateRef State = C.getState();
820b57cec5SDimitry Andric RegionStateTy RegionStates = State->get<RegionState>();
8306c3fb27SDimitry Andric for (const MemRegion *Reg: llvm::make_first_range(RegionStates)) {
8406c3fb27SDimitry Andric if (!SR.isLiveRegion(Reg))
8506c3fb27SDimitry Andric State = State->remove<RegionState>(Reg);
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric C.addTransition(State);*/
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric
getKindOfNewOp(const CXXNewExpr * NE,const FunctionDecl * FD) const900b57cec5SDimitry Andric AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
910b57cec5SDimitry Andric const FunctionDecl *FD) const {
920b57cec5SDimitry Andric // This checker try not to assume anything about placement and overloaded
930b57cec5SDimitry Andric // new to avoid false positives.
940b57cec5SDimitry Andric if (isa<CXXMethodDecl>(FD))
950b57cec5SDimitry Andric return AllocKind::Unknown;
960b57cec5SDimitry Andric if (FD->getNumParams() != 1 || FD->isVariadic())
970b57cec5SDimitry Andric return AllocKind::Unknown;
980b57cec5SDimitry Andric if (NE->isArray())
990b57cec5SDimitry Andric return AllocKind::Array;
1000b57cec5SDimitry Andric
1010b57cec5SDimitry Andric return AllocKind::SingleObject;
1020b57cec5SDimitry Andric }
1030b57cec5SDimitry Andric
1040b57cec5SDimitry Andric const MemRegion *
getPointedRegion(const MemRegion * Region,CheckerContext & C) const1050b57cec5SDimitry Andric PointerArithChecker::getPointedRegion(const MemRegion *Region,
1060b57cec5SDimitry Andric CheckerContext &C) const {
1070b57cec5SDimitry Andric assert(Region);
1080b57cec5SDimitry Andric ProgramStateRef State = C.getState();
1090b57cec5SDimitry Andric SVal S = State->getSVal(Region);
1100b57cec5SDimitry Andric return S.getAsRegion();
1110b57cec5SDimitry Andric }
1120b57cec5SDimitry Andric
1130b57cec5SDimitry Andric /// Checks whether a region is the part of an array.
1140b57cec5SDimitry Andric /// In case there is a derived to base cast above the array element, the
1150b57cec5SDimitry Andric /// Polymorphic output value is set to true. AKind output value is set to the
1160b57cec5SDimitry Andric /// allocation kind of the inspected region.
getArrayRegion(const MemRegion * Region,bool & Polymorphic,AllocKind & AKind,CheckerContext & C) const1170b57cec5SDimitry Andric const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
1180b57cec5SDimitry Andric bool &Polymorphic,
1190b57cec5SDimitry Andric AllocKind &AKind,
1200b57cec5SDimitry Andric CheckerContext &C) const {
1210b57cec5SDimitry Andric assert(Region);
122a7dea167SDimitry Andric while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
123a7dea167SDimitry Andric Region = BaseRegion->getSuperRegion();
1240b57cec5SDimitry Andric Polymorphic = true;
1250b57cec5SDimitry Andric }
126a7dea167SDimitry Andric if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
127a7dea167SDimitry Andric Region = ElemRegion->getSuperRegion();
1280b57cec5SDimitry Andric }
1290b57cec5SDimitry Andric
1300b57cec5SDimitry Andric ProgramStateRef State = C.getState();
1310b57cec5SDimitry Andric if (const AllocKind *Kind = State->get<RegionState>(Region)) {
1320b57cec5SDimitry Andric AKind = *Kind;
1330b57cec5SDimitry Andric if (*Kind == AllocKind::Array)
1340b57cec5SDimitry Andric return Region;
1350b57cec5SDimitry Andric else
1360b57cec5SDimitry Andric return nullptr;
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric // When the region is symbolic and we do not have any information about it,
1390b57cec5SDimitry Andric // assume that this is an array to avoid false positives.
140a7dea167SDimitry Andric if (isa<SymbolicRegion>(Region))
1410b57cec5SDimitry Andric return Region;
1420b57cec5SDimitry Andric
1430b57cec5SDimitry Andric // No AllocKind stored and not symbolic, assume that it points to a single
1440b57cec5SDimitry Andric // object.
1450b57cec5SDimitry Andric return nullptr;
1460b57cec5SDimitry Andric }
1470b57cec5SDimitry Andric
reportPointerArithMisuse(const Expr * E,CheckerContext & C,bool PointedNeeded) const1480b57cec5SDimitry Andric void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
1490b57cec5SDimitry Andric CheckerContext &C,
1500b57cec5SDimitry Andric bool PointedNeeded) const {
1510b57cec5SDimitry Andric SourceRange SR = E->getSourceRange();
1520b57cec5SDimitry Andric if (SR.isInvalid())
1530b57cec5SDimitry Andric return;
1540b57cec5SDimitry Andric
1550b57cec5SDimitry Andric ProgramStateRef State = C.getState();
1560b57cec5SDimitry Andric const MemRegion *Region = C.getSVal(E).getAsRegion();
1570b57cec5SDimitry Andric if (!Region)
1580b57cec5SDimitry Andric return;
1590b57cec5SDimitry Andric if (PointedNeeded)
1600b57cec5SDimitry Andric Region = getPointedRegion(Region, C);
1610b57cec5SDimitry Andric if (!Region)
1620b57cec5SDimitry Andric return;
1630b57cec5SDimitry Andric
1640b57cec5SDimitry Andric bool IsPolymorphic = false;
1650b57cec5SDimitry Andric AllocKind Kind = AllocKind::Unknown;
1660b57cec5SDimitry Andric if (const MemRegion *ArrayRegion =
1670b57cec5SDimitry Andric getArrayRegion(Region, IsPolymorphic, Kind, C)) {
1680b57cec5SDimitry Andric if (!IsPolymorphic)
1690b57cec5SDimitry Andric return;
1700b57cec5SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
1715f757f3fSDimitry Andric constexpr llvm::StringLiteral Msg =
1720b57cec5SDimitry Andric "Pointer arithmetic on a pointer to base class is dangerous "
1735f757f3fSDimitry Andric "because derived and base class may have different size.";
174647cbc5dSDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(BT_polyArray, Msg, N);
1750b57cec5SDimitry Andric R->addRange(E->getSourceRange());
1760b57cec5SDimitry Andric R->markInteresting(ArrayRegion);
1770b57cec5SDimitry Andric C.emitReport(std::move(R));
1780b57cec5SDimitry Andric }
1790b57cec5SDimitry Andric return;
1800b57cec5SDimitry Andric }
1810b57cec5SDimitry Andric
1820b57cec5SDimitry Andric if (Kind == AllocKind::Reinterpreted)
1830b57cec5SDimitry Andric return;
1840b57cec5SDimitry Andric
1850b57cec5SDimitry Andric // We might not have enough information about symbolic regions.
1860b57cec5SDimitry Andric if (Kind != AllocKind::SingleObject &&
1870b57cec5SDimitry Andric Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
1880b57cec5SDimitry Andric return;
1890b57cec5SDimitry Andric
1900b57cec5SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
1915f757f3fSDimitry Andric constexpr llvm::StringLiteral Msg =
1925f757f3fSDimitry Andric "Pointer arithmetic on non-array variables relies on memory layout, "
1935f757f3fSDimitry Andric "which is dangerous.";
194647cbc5dSDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(BT_pointerArith, Msg, N);
1950b57cec5SDimitry Andric R->addRange(SR);
1960b57cec5SDimitry Andric R->markInteresting(Region);
1970b57cec5SDimitry Andric C.emitReport(std::move(R));
1980b57cec5SDimitry Andric }
1990b57cec5SDimitry Andric }
2000b57cec5SDimitry Andric
initAllocIdentifiers(ASTContext & C) const2010b57cec5SDimitry Andric void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
2020b57cec5SDimitry Andric if (!AllocFunctions.empty())
2030b57cec5SDimitry Andric return;
2040b57cec5SDimitry Andric AllocFunctions.insert(&C.Idents.get("alloca"));
2050b57cec5SDimitry Andric AllocFunctions.insert(&C.Idents.get("malloc"));
2060b57cec5SDimitry Andric AllocFunctions.insert(&C.Idents.get("realloc"));
2070b57cec5SDimitry Andric AllocFunctions.insert(&C.Idents.get("calloc"));
2080b57cec5SDimitry Andric AllocFunctions.insert(&C.Idents.get("valloc"));
2090b57cec5SDimitry Andric }
2100b57cec5SDimitry Andric
checkPostStmt(const CallExpr * CE,CheckerContext & C) const2110b57cec5SDimitry Andric void PointerArithChecker::checkPostStmt(const CallExpr *CE,
2120b57cec5SDimitry Andric CheckerContext &C) const {
2130b57cec5SDimitry Andric ProgramStateRef State = C.getState();
2140b57cec5SDimitry Andric const FunctionDecl *FD = C.getCalleeDecl(CE);
2150b57cec5SDimitry Andric if (!FD)
2160b57cec5SDimitry Andric return;
2170b57cec5SDimitry Andric IdentifierInfo *FunI = FD->getIdentifier();
2180b57cec5SDimitry Andric initAllocIdentifiers(C.getASTContext());
2190b57cec5SDimitry Andric if (AllocFunctions.count(FunI) == 0)
2200b57cec5SDimitry Andric return;
2210b57cec5SDimitry Andric
2220b57cec5SDimitry Andric SVal SV = C.getSVal(CE);
2230b57cec5SDimitry Andric const MemRegion *Region = SV.getAsRegion();
2240b57cec5SDimitry Andric if (!Region)
2250b57cec5SDimitry Andric return;
2260b57cec5SDimitry Andric // Assume that C allocation functions allocate arrays to avoid false
2270b57cec5SDimitry Andric // positives.
2280b57cec5SDimitry Andric // TODO: Add heuristics to distinguish alloc calls that allocates single
2290b57cec5SDimitry Andric // objecs.
2300b57cec5SDimitry Andric State = State->set<RegionState>(Region, AllocKind::Array);
2310b57cec5SDimitry Andric C.addTransition(State);
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric
checkPostStmt(const CXXNewExpr * NE,CheckerContext & C) const2340b57cec5SDimitry Andric void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
2350b57cec5SDimitry Andric CheckerContext &C) const {
2360b57cec5SDimitry Andric const FunctionDecl *FD = NE->getOperatorNew();
2370b57cec5SDimitry Andric if (!FD)
2380b57cec5SDimitry Andric return;
2390b57cec5SDimitry Andric
2400b57cec5SDimitry Andric AllocKind Kind = getKindOfNewOp(NE, FD);
2410b57cec5SDimitry Andric
2420b57cec5SDimitry Andric ProgramStateRef State = C.getState();
2430b57cec5SDimitry Andric SVal AllocedVal = C.getSVal(NE);
2440b57cec5SDimitry Andric const MemRegion *Region = AllocedVal.getAsRegion();
2450b57cec5SDimitry Andric if (!Region)
2460b57cec5SDimitry Andric return;
2470b57cec5SDimitry Andric State = State->set<RegionState>(Region, Kind);
2480b57cec5SDimitry Andric C.addTransition(State);
2490b57cec5SDimitry Andric }
2500b57cec5SDimitry Andric
checkPostStmt(const CastExpr * CE,CheckerContext & C) const2510b57cec5SDimitry Andric void PointerArithChecker::checkPostStmt(const CastExpr *CE,
2520b57cec5SDimitry Andric CheckerContext &C) const {
2530b57cec5SDimitry Andric if (CE->getCastKind() != CastKind::CK_BitCast)
2540b57cec5SDimitry Andric return;
2550b57cec5SDimitry Andric
2560b57cec5SDimitry Andric const Expr *CastedExpr = CE->getSubExpr();
2570b57cec5SDimitry Andric ProgramStateRef State = C.getState();
2580b57cec5SDimitry Andric SVal CastedVal = C.getSVal(CastedExpr);
2590b57cec5SDimitry Andric
2600b57cec5SDimitry Andric const MemRegion *Region = CastedVal.getAsRegion();
2610b57cec5SDimitry Andric if (!Region)
2620b57cec5SDimitry Andric return;
2630b57cec5SDimitry Andric
2640b57cec5SDimitry Andric // Suppress reinterpret casted hits.
2650b57cec5SDimitry Andric State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
2660b57cec5SDimitry Andric C.addTransition(State);
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric
checkPreStmt(const CastExpr * CE,CheckerContext & C) const2690b57cec5SDimitry Andric void PointerArithChecker::checkPreStmt(const CastExpr *CE,
2700b57cec5SDimitry Andric CheckerContext &C) const {
2710b57cec5SDimitry Andric if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
2720b57cec5SDimitry Andric return;
2730b57cec5SDimitry Andric
2740b57cec5SDimitry Andric const Expr *CastedExpr = CE->getSubExpr();
2750b57cec5SDimitry Andric ProgramStateRef State = C.getState();
2760b57cec5SDimitry Andric SVal CastedVal = C.getSVal(CastedExpr);
2770b57cec5SDimitry Andric
2780b57cec5SDimitry Andric const MemRegion *Region = CastedVal.getAsRegion();
2790b57cec5SDimitry Andric if (!Region)
2800b57cec5SDimitry Andric return;
2810b57cec5SDimitry Andric
2820b57cec5SDimitry Andric if (const AllocKind *Kind = State->get<RegionState>(Region)) {
2830b57cec5SDimitry Andric if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
2840b57cec5SDimitry Andric return;
2850b57cec5SDimitry Andric }
2860b57cec5SDimitry Andric State = State->set<RegionState>(Region, AllocKind::Array);
2870b57cec5SDimitry Andric C.addTransition(State);
2880b57cec5SDimitry Andric }
2890b57cec5SDimitry Andric
checkPreStmt(const UnaryOperator * UOp,CheckerContext & C) const2900b57cec5SDimitry Andric void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
2910b57cec5SDimitry Andric CheckerContext &C) const {
2920b57cec5SDimitry Andric if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
2930b57cec5SDimitry Andric return;
2940b57cec5SDimitry Andric reportPointerArithMisuse(UOp->getSubExpr(), C, true);
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric
checkPreStmt(const ArraySubscriptExpr * SubsExpr,CheckerContext & C) const2970b57cec5SDimitry Andric void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
2980b57cec5SDimitry Andric CheckerContext &C) const {
2990b57cec5SDimitry Andric SVal Idx = C.getSVal(SubsExpr->getIdx());
3000b57cec5SDimitry Andric
3010b57cec5SDimitry Andric // Indexing with 0 is OK.
3020b57cec5SDimitry Andric if (Idx.isZeroConstant())
3030b57cec5SDimitry Andric return;
3040b57cec5SDimitry Andric
3050b57cec5SDimitry Andric // Indexing vector-type expressions is also OK.
3060b57cec5SDimitry Andric if (SubsExpr->getBase()->getType()->isVectorType())
3070b57cec5SDimitry Andric return;
3080b57cec5SDimitry Andric reportPointerArithMisuse(SubsExpr->getBase(), C);
3090b57cec5SDimitry Andric }
3100b57cec5SDimitry Andric
checkPreStmt(const BinaryOperator * BOp,CheckerContext & C) const3110b57cec5SDimitry Andric void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
3120b57cec5SDimitry Andric CheckerContext &C) const {
3130b57cec5SDimitry Andric BinaryOperatorKind OpKind = BOp->getOpcode();
3140b57cec5SDimitry Andric if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
3150b57cec5SDimitry Andric return;
3160b57cec5SDimitry Andric
3170b57cec5SDimitry Andric const Expr *Lhs = BOp->getLHS();
3180b57cec5SDimitry Andric const Expr *Rhs = BOp->getRHS();
3190b57cec5SDimitry Andric ProgramStateRef State = C.getState();
3200b57cec5SDimitry Andric
3210b57cec5SDimitry Andric if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
3220b57cec5SDimitry Andric SVal RHSVal = C.getSVal(Rhs);
3230b57cec5SDimitry Andric if (State->isNull(RHSVal).isConstrainedTrue())
3240b57cec5SDimitry Andric return;
3250b57cec5SDimitry Andric reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
3260b57cec5SDimitry Andric }
3270b57cec5SDimitry Andric // The int += ptr; case is not valid C++.
3280b57cec5SDimitry Andric if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
3290b57cec5SDimitry Andric SVal LHSVal = C.getSVal(Lhs);
3300b57cec5SDimitry Andric if (State->isNull(LHSVal).isConstrainedTrue())
3310b57cec5SDimitry Andric return;
3320b57cec5SDimitry Andric reportPointerArithMisuse(Rhs, C);
3330b57cec5SDimitry Andric }
3340b57cec5SDimitry Andric }
3350b57cec5SDimitry Andric
registerPointerArithChecker(CheckerManager & mgr)3360b57cec5SDimitry Andric void ento::registerPointerArithChecker(CheckerManager &mgr) {
3370b57cec5SDimitry Andric mgr.registerChecker<PointerArithChecker>();
3380b57cec5SDimitry Andric }
3390b57cec5SDimitry Andric
shouldRegisterPointerArithChecker(const CheckerManager & mgr)3405ffd83dbSDimitry Andric bool ento::shouldRegisterPointerArithChecker(const CheckerManager &mgr) {
3410b57cec5SDimitry Andric return true;
3420b57cec5SDimitry Andric }
343