]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
Update clang to trunk r290819 and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / PointerArithChecker.cpp
1 //=== 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
15 #include "ClangSACheckers.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/ExprCXX.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22
23 using namespace clang;
24 using namespace ento;
25
26 namespace {
27 enum class AllocKind {
28   SingleObject,
29   Array,
30   Unknown,
31   Reinterpreted // Single object interpreted as an array.
32 };
33 } // end namespace
34
35 namespace llvm {
36 template <> 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
43 namespace {
44 class PointerArithChecker
45     : 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;
62
63 public:
64   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;
72 };
73 } // end namespace
74
75 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
76
77 void 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);*/
89 }
90
91 AllocKind 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
105 const MemRegion *
106 PointerArithChecker::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.
118 const 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
149 void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
150                                                    CheckerContext &C,
151                                                    bool PointedNeeded) const {
152   SourceRange SR = E->getSourceRange();
153   if (SR.isInvalid())
154     return;
155
156   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)
164     return;
165
166   bool IsPolymorphic = false;
167   AllocKind Kind = AllocKind::Unknown;
168   if (const MemRegion *ArrayRegion =
169           getArrayRegion(Region, IsPolymorphic, Kind, C)) {
170     if (!IsPolymorphic)
171       return;
172     if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
173       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);
182       C.emitReport(std::move(R));
183     }
184     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
209 void 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
219 void 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
242 void 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
259 void 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
277 void 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
298 void 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
305 void 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
316 void 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);
338   }
339 }
340
341 void ento::registerPointerArithChecker(CheckerManager &mgr) {
342   mgr.registerChecker<PointerArithChecker>();
343 }