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