1*67e74705SXin Li //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // This files defines PointerArithChecker, a builtin checker that checks for
11*67e74705SXin Li // pointer arithmetic on locations other than array elements.
12*67e74705SXin Li //
13*67e74705SXin Li //===----------------------------------------------------------------------===//
14*67e74705SXin Li
15*67e74705SXin Li #include "ClangSACheckers.h"
16*67e74705SXin Li #include "clang/AST/DeclCXX.h"
17*67e74705SXin Li #include "clang/AST/ExprCXX.h"
18*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22*67e74705SXin Li #include "llvm/ADT/SmallVector.h"
23*67e74705SXin Li
24*67e74705SXin Li using namespace clang;
25*67e74705SXin Li using namespace ento;
26*67e74705SXin Li
27*67e74705SXin Li namespace {
28*67e74705SXin Li enum class AllocKind {
29*67e74705SXin Li SingleObject,
30*67e74705SXin Li Array,
31*67e74705SXin Li Unknown,
32*67e74705SXin Li Reinterpreted // Single object interpreted as an array.
33*67e74705SXin Li };
34*67e74705SXin Li } // end namespace
35*67e74705SXin Li
36*67e74705SXin Li namespace llvm {
37*67e74705SXin Li template <> struct FoldingSetTrait<AllocKind> {
Profilellvm::FoldingSetTrait38*67e74705SXin Li static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
39*67e74705SXin Li ID.AddInteger(static_cast<int>(X));
40*67e74705SXin Li }
41*67e74705SXin Li };
42*67e74705SXin Li } // end namespace llvm
43*67e74705SXin Li
44*67e74705SXin Li namespace {
45*67e74705SXin Li class PointerArithChecker
46*67e74705SXin Li : public Checker<
47*67e74705SXin Li check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
48*67e74705SXin Li check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
49*67e74705SXin Li check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
50*67e74705SXin Li check::PostStmt<CallExpr>, check::DeadSymbols> {
51*67e74705SXin Li AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
52*67e74705SXin Li const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
53*67e74705SXin Li AllocKind &AKind, CheckerContext &C) const;
54*67e74705SXin Li const MemRegion *getPointedRegion(const MemRegion *Region,
55*67e74705SXin Li CheckerContext &C) const;
56*67e74705SXin Li void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
57*67e74705SXin Li bool PointedNeeded = false) const;
58*67e74705SXin Li void initAllocIdentifiers(ASTContext &C) const;
59*67e74705SXin Li
60*67e74705SXin Li mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
61*67e74705SXin Li mutable std::unique_ptr<BuiltinBug> BT_polyArray;
62*67e74705SXin Li mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
63*67e74705SXin Li
64*67e74705SXin Li public:
65*67e74705SXin Li void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
66*67e74705SXin Li void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
67*67e74705SXin Li void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
68*67e74705SXin Li void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
69*67e74705SXin Li void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
70*67e74705SXin Li void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
71*67e74705SXin Li void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
72*67e74705SXin Li void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
73*67e74705SXin Li };
74*67e74705SXin Li } // end namespace
75*67e74705SXin Li
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState,const MemRegion *,AllocKind)76*67e74705SXin Li REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
77*67e74705SXin Li
78*67e74705SXin Li void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
79*67e74705SXin Li CheckerContext &C) const {
80*67e74705SXin Li // TODO: intentional leak. Some information is garbage collected too early,
81*67e74705SXin Li // see http://reviews.llvm.org/D14203 for further information.
82*67e74705SXin Li /*ProgramStateRef State = C.getState();
83*67e74705SXin Li RegionStateTy RegionStates = State->get<RegionState>();
84*67e74705SXin Li for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
85*67e74705SXin Li I != E; ++I) {
86*67e74705SXin Li if (!SR.isLiveRegion(I->first))
87*67e74705SXin Li State = State->remove<RegionState>(I->first);
88*67e74705SXin Li }
89*67e74705SXin Li C.addTransition(State);*/
90*67e74705SXin Li }
91*67e74705SXin Li
getKindOfNewOp(const CXXNewExpr * NE,const FunctionDecl * FD) const92*67e74705SXin Li AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
93*67e74705SXin Li const FunctionDecl *FD) const {
94*67e74705SXin Li // This checker try not to assume anything about placement and overloaded
95*67e74705SXin Li // new to avoid false positives.
96*67e74705SXin Li if (isa<CXXMethodDecl>(FD))
97*67e74705SXin Li return AllocKind::Unknown;
98*67e74705SXin Li if (FD->getNumParams() != 1 || FD->isVariadic())
99*67e74705SXin Li return AllocKind::Unknown;
100*67e74705SXin Li if (NE->isArray())
101*67e74705SXin Li return AllocKind::Array;
102*67e74705SXin Li
103*67e74705SXin Li return AllocKind::SingleObject;
104*67e74705SXin Li }
105*67e74705SXin Li
106*67e74705SXin Li const MemRegion *
getPointedRegion(const MemRegion * Region,CheckerContext & C) const107*67e74705SXin Li PointerArithChecker::getPointedRegion(const MemRegion *Region,
108*67e74705SXin Li CheckerContext &C) const {
109*67e74705SXin Li assert(Region);
110*67e74705SXin Li ProgramStateRef State = C.getState();
111*67e74705SXin Li SVal S = State->getSVal(Region);
112*67e74705SXin Li return S.getAsRegion();
113*67e74705SXin Li }
114*67e74705SXin Li
115*67e74705SXin Li /// Checks whether a region is the part of an array.
116*67e74705SXin Li /// In case there is a dericed to base cast above the array element, the
117*67e74705SXin Li /// Polymorphic output value is set to true. AKind output value is set to the
118*67e74705SXin Li /// allocation kind of the inspected region.
getArrayRegion(const MemRegion * Region,bool & Polymorphic,AllocKind & AKind,CheckerContext & C) const119*67e74705SXin Li const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
120*67e74705SXin Li bool &Polymorphic,
121*67e74705SXin Li AllocKind &AKind,
122*67e74705SXin Li CheckerContext &C) const {
123*67e74705SXin Li assert(Region);
124*67e74705SXin Li while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) {
125*67e74705SXin Li Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion();
126*67e74705SXin Li Polymorphic = true;
127*67e74705SXin Li }
128*67e74705SXin Li if (Region->getKind() == MemRegion::Kind::ElementRegionKind) {
129*67e74705SXin Li Region = Region->getAs<ElementRegion>()->getSuperRegion();
130*67e74705SXin Li }
131*67e74705SXin Li
132*67e74705SXin Li ProgramStateRef State = C.getState();
133*67e74705SXin Li if (const AllocKind *Kind = State->get<RegionState>(Region)) {
134*67e74705SXin Li AKind = *Kind;
135*67e74705SXin Li if (*Kind == AllocKind::Array)
136*67e74705SXin Li return Region;
137*67e74705SXin Li else
138*67e74705SXin Li return nullptr;
139*67e74705SXin Li }
140*67e74705SXin Li // When the region is symbolic and we do not have any information about it,
141*67e74705SXin Li // assume that this is an array to avoid false positives.
142*67e74705SXin Li if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
143*67e74705SXin Li return Region;
144*67e74705SXin Li
145*67e74705SXin Li // No AllocKind stored and not symbolic, assume that it points to a single
146*67e74705SXin Li // object.
147*67e74705SXin Li return nullptr;
148*67e74705SXin Li }
149*67e74705SXin Li
reportPointerArithMisuse(const Expr * E,CheckerContext & C,bool PointedNeeded) const150*67e74705SXin Li void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
151*67e74705SXin Li CheckerContext &C,
152*67e74705SXin Li bool PointedNeeded) const {
153*67e74705SXin Li SourceRange SR = E->getSourceRange();
154*67e74705SXin Li if (SR.isInvalid())
155*67e74705SXin Li return;
156*67e74705SXin Li
157*67e74705SXin Li ProgramStateRef State = C.getState();
158*67e74705SXin Li const MemRegion *Region =
159*67e74705SXin Li State->getSVal(E, C.getLocationContext()).getAsRegion();
160*67e74705SXin Li if (!Region)
161*67e74705SXin Li return;
162*67e74705SXin Li if (PointedNeeded)
163*67e74705SXin Li Region = getPointedRegion(Region, C);
164*67e74705SXin Li if (!Region)
165*67e74705SXin Li return;
166*67e74705SXin Li
167*67e74705SXin Li bool IsPolymorphic = false;
168*67e74705SXin Li AllocKind Kind = AllocKind::Unknown;
169*67e74705SXin Li if (const MemRegion *ArrayRegion =
170*67e74705SXin Li getArrayRegion(Region, IsPolymorphic, Kind, C)) {
171*67e74705SXin Li if (!IsPolymorphic)
172*67e74705SXin Li return;
173*67e74705SXin Li if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
174*67e74705SXin Li if (!BT_polyArray)
175*67e74705SXin Li BT_polyArray.reset(new BuiltinBug(
176*67e74705SXin Li this, "Dangerous pointer arithmetic",
177*67e74705SXin Li "Pointer arithmetic on a pointer to base class is dangerous "
178*67e74705SXin Li "because derived and base class may have different size."));
179*67e74705SXin Li auto R = llvm::make_unique<BugReport>(*BT_polyArray,
180*67e74705SXin Li BT_polyArray->getDescription(), N);
181*67e74705SXin Li R->addRange(E->getSourceRange());
182*67e74705SXin Li R->markInteresting(ArrayRegion);
183*67e74705SXin Li C.emitReport(std::move(R));
184*67e74705SXin Li }
185*67e74705SXin Li return;
186*67e74705SXin Li }
187*67e74705SXin Li
188*67e74705SXin Li if (Kind == AllocKind::Reinterpreted)
189*67e74705SXin Li return;
190*67e74705SXin Li
191*67e74705SXin Li // We might not have enough information about symbolic regions.
192*67e74705SXin Li if (Kind != AllocKind::SingleObject &&
193*67e74705SXin Li Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
194*67e74705SXin Li return;
195*67e74705SXin Li
196*67e74705SXin Li if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
197*67e74705SXin Li if (!BT_pointerArith)
198*67e74705SXin Li BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
199*67e74705SXin Li "Pointer arithmetic on non-array "
200*67e74705SXin Li "variables relies on memory layout, "
201*67e74705SXin Li "which is dangerous."));
202*67e74705SXin Li auto R = llvm::make_unique<BugReport>(*BT_pointerArith,
203*67e74705SXin Li BT_pointerArith->getDescription(), N);
204*67e74705SXin Li R->addRange(SR);
205*67e74705SXin Li R->markInteresting(Region);
206*67e74705SXin Li C.emitReport(std::move(R));
207*67e74705SXin Li }
208*67e74705SXin Li }
209*67e74705SXin Li
initAllocIdentifiers(ASTContext & C) const210*67e74705SXin Li void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
211*67e74705SXin Li if (!AllocFunctions.empty())
212*67e74705SXin Li return;
213*67e74705SXin Li AllocFunctions.insert(&C.Idents.get("alloca"));
214*67e74705SXin Li AllocFunctions.insert(&C.Idents.get("malloc"));
215*67e74705SXin Li AllocFunctions.insert(&C.Idents.get("realloc"));
216*67e74705SXin Li AllocFunctions.insert(&C.Idents.get("calloc"));
217*67e74705SXin Li AllocFunctions.insert(&C.Idents.get("valloc"));
218*67e74705SXin Li }
219*67e74705SXin Li
checkPostStmt(const CallExpr * CE,CheckerContext & C) const220*67e74705SXin Li void PointerArithChecker::checkPostStmt(const CallExpr *CE,
221*67e74705SXin Li CheckerContext &C) const {
222*67e74705SXin Li ProgramStateRef State = C.getState();
223*67e74705SXin Li const FunctionDecl *FD = C.getCalleeDecl(CE);
224*67e74705SXin Li if (!FD)
225*67e74705SXin Li return;
226*67e74705SXin Li IdentifierInfo *FunI = FD->getIdentifier();
227*67e74705SXin Li initAllocIdentifiers(C.getASTContext());
228*67e74705SXin Li if (AllocFunctions.count(FunI) == 0)
229*67e74705SXin Li return;
230*67e74705SXin Li
231*67e74705SXin Li SVal SV = State->getSVal(CE, C.getLocationContext());
232*67e74705SXin Li const MemRegion *Region = SV.getAsRegion();
233*67e74705SXin Li if (!Region)
234*67e74705SXin Li return;
235*67e74705SXin Li // Assume that C allocation functions allocate arrays to avoid false
236*67e74705SXin Li // positives.
237*67e74705SXin Li // TODO: Add heuristics to distinguish alloc calls that allocates single
238*67e74705SXin Li // objecs.
239*67e74705SXin Li State = State->set<RegionState>(Region, AllocKind::Array);
240*67e74705SXin Li C.addTransition(State);
241*67e74705SXin Li }
242*67e74705SXin Li
checkPostStmt(const CXXNewExpr * NE,CheckerContext & C) const243*67e74705SXin Li void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
244*67e74705SXin Li CheckerContext &C) const {
245*67e74705SXin Li const FunctionDecl *FD = NE->getOperatorNew();
246*67e74705SXin Li if (!FD)
247*67e74705SXin Li return;
248*67e74705SXin Li
249*67e74705SXin Li AllocKind Kind = getKindOfNewOp(NE, FD);
250*67e74705SXin Li
251*67e74705SXin Li ProgramStateRef State = C.getState();
252*67e74705SXin Li SVal AllocedVal = State->getSVal(NE, C.getLocationContext());
253*67e74705SXin Li const MemRegion *Region = AllocedVal.getAsRegion();
254*67e74705SXin Li if (!Region)
255*67e74705SXin Li return;
256*67e74705SXin Li State = State->set<RegionState>(Region, Kind);
257*67e74705SXin Li C.addTransition(State);
258*67e74705SXin Li }
259*67e74705SXin Li
checkPostStmt(const CastExpr * CE,CheckerContext & C) const260*67e74705SXin Li void PointerArithChecker::checkPostStmt(const CastExpr *CE,
261*67e74705SXin Li CheckerContext &C) const {
262*67e74705SXin Li if (CE->getCastKind() != CastKind::CK_BitCast)
263*67e74705SXin Li return;
264*67e74705SXin Li
265*67e74705SXin Li const Expr *CastedExpr = CE->getSubExpr();
266*67e74705SXin Li ProgramStateRef State = C.getState();
267*67e74705SXin Li SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
268*67e74705SXin Li
269*67e74705SXin Li const MemRegion *Region = CastedVal.getAsRegion();
270*67e74705SXin Li if (!Region)
271*67e74705SXin Li return;
272*67e74705SXin Li
273*67e74705SXin Li // Suppress reinterpret casted hits.
274*67e74705SXin Li State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
275*67e74705SXin Li C.addTransition(State);
276*67e74705SXin Li }
277*67e74705SXin Li
checkPreStmt(const CastExpr * CE,CheckerContext & C) const278*67e74705SXin Li void PointerArithChecker::checkPreStmt(const CastExpr *CE,
279*67e74705SXin Li CheckerContext &C) const {
280*67e74705SXin Li if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
281*67e74705SXin Li return;
282*67e74705SXin Li
283*67e74705SXin Li const Expr *CastedExpr = CE->getSubExpr();
284*67e74705SXin Li ProgramStateRef State = C.getState();
285*67e74705SXin Li SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
286*67e74705SXin Li
287*67e74705SXin Li const MemRegion *Region = CastedVal.getAsRegion();
288*67e74705SXin Li if (!Region)
289*67e74705SXin Li return;
290*67e74705SXin Li
291*67e74705SXin Li if (const AllocKind *Kind = State->get<RegionState>(Region)) {
292*67e74705SXin Li if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
293*67e74705SXin Li return;
294*67e74705SXin Li }
295*67e74705SXin Li State = State->set<RegionState>(Region, AllocKind::Array);
296*67e74705SXin Li C.addTransition(State);
297*67e74705SXin Li }
298*67e74705SXin Li
checkPreStmt(const UnaryOperator * UOp,CheckerContext & C) const299*67e74705SXin Li void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
300*67e74705SXin Li CheckerContext &C) const {
301*67e74705SXin Li if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
302*67e74705SXin Li return;
303*67e74705SXin Li reportPointerArithMisuse(UOp->getSubExpr(), C, true);
304*67e74705SXin Li }
305*67e74705SXin Li
checkPreStmt(const ArraySubscriptExpr * SubsExpr,CheckerContext & C) const306*67e74705SXin Li void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
307*67e74705SXin Li CheckerContext &C) const {
308*67e74705SXin Li ProgramStateRef State = C.getState();
309*67e74705SXin Li SVal Idx = State->getSVal(SubsExpr->getIdx(), C.getLocationContext());
310*67e74705SXin Li
311*67e74705SXin Li // Indexing with 0 is OK.
312*67e74705SXin Li if (Idx.isZeroConstant())
313*67e74705SXin Li return;
314*67e74705SXin Li reportPointerArithMisuse(SubsExpr->getBase(), C);
315*67e74705SXin Li }
316*67e74705SXin Li
checkPreStmt(const BinaryOperator * BOp,CheckerContext & C) const317*67e74705SXin Li void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
318*67e74705SXin Li CheckerContext &C) const {
319*67e74705SXin Li BinaryOperatorKind OpKind = BOp->getOpcode();
320*67e74705SXin Li if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
321*67e74705SXin Li return;
322*67e74705SXin Li
323*67e74705SXin Li const Expr *Lhs = BOp->getLHS();
324*67e74705SXin Li const Expr *Rhs = BOp->getRHS();
325*67e74705SXin Li ProgramStateRef State = C.getState();
326*67e74705SXin Li
327*67e74705SXin Li if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
328*67e74705SXin Li SVal RHSVal = State->getSVal(Rhs, C.getLocationContext());
329*67e74705SXin Li if (State->isNull(RHSVal).isConstrainedTrue())
330*67e74705SXin Li return;
331*67e74705SXin Li reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
332*67e74705SXin Li }
333*67e74705SXin Li // The int += ptr; case is not valid C++.
334*67e74705SXin Li if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
335*67e74705SXin Li SVal LHSVal = State->getSVal(Lhs, C.getLocationContext());
336*67e74705SXin Li if (State->isNull(LHSVal).isConstrainedTrue())
337*67e74705SXin Li return;
338*67e74705SXin Li reportPointerArithMisuse(Rhs, C);
339*67e74705SXin Li }
340*67e74705SXin Li }
341*67e74705SXin Li
registerPointerArithChecker(CheckerManager & mgr)342*67e74705SXin Li void ento::registerPointerArithChecker(CheckerManager &mgr) {
343*67e74705SXin Li mgr.registerChecker<PointerArithChecker>();
344*67e74705SXin Li }
345