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