clang  10.0.0svn
PointerArithChecker.cpp
Go to the documentation of this file.
1 //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This files defines PointerArithChecker, a builtin checker that checks for
10 // pointer arithmetic on locations other than array elements.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 #include "clang/AST/DeclCXX.h"
16 #include "clang/AST/ExprCXX.h"
21 
22 using namespace clang;
23 using namespace ento;
24 
25 namespace {
26 enum class AllocKind {
27  SingleObject,
28  Array,
29  Unknown,
30  Reinterpreted // Single object interpreted as an array.
31 };
32 } // end namespace
33 
34 namespace llvm {
35 template <> struct FoldingSetTrait<AllocKind> {
36  static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
37  ID.AddInteger(static_cast<int>(X));
38  }
39 };
40 } // end namespace llvm
41 
42 namespace {
43 class PointerArithChecker
44  : public Checker<
45  check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
46  check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
47  check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
48  check::PostStmt<CallExpr>, check::DeadSymbols> {
49  AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
50  const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
51  AllocKind &AKind, CheckerContext &C) const;
52  const MemRegion *getPointedRegion(const MemRegion *Region,
53  CheckerContext &C) const;
54  void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
55  bool PointedNeeded = false) const;
56  void initAllocIdentifiers(ASTContext &C) const;
57 
58  mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
59  mutable std::unique_ptr<BuiltinBug> BT_polyArray;
60  mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
61 
62 public:
63  void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
64  void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
65  void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
66  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
67  void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
68  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
69  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
70  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
71 };
72 } // end namespace
73 
74 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
75 
76 void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
77  CheckerContext &C) const {
78  // TODO: intentional leak. Some information is garbage collected too early,
79  // see http://reviews.llvm.org/D14203 for further information.
80  /*ProgramStateRef State = C.getState();
81  RegionStateTy RegionStates = State->get<RegionState>();
82  for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
83  I != E; ++I) {
84  if (!SR.isLiveRegion(I->first))
85  State = State->remove<RegionState>(I->first);
86  }
87  C.addTransition(State);*/
88 }
89 
90 AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
91  const FunctionDecl *FD) const {
92  // This checker try not to assume anything about placement and overloaded
93  // new to avoid false positives.
94  if (isa<CXXMethodDecl>(FD))
95  return AllocKind::Unknown;
96  if (FD->getNumParams() != 1 || FD->isVariadic())
97  return AllocKind::Unknown;
98  if (NE->isArray())
99  return AllocKind::Array;
100 
101  return AllocKind::SingleObject;
102 }
103 
104 const MemRegion *
105 PointerArithChecker::getPointedRegion(const MemRegion *Region,
106  CheckerContext &C) const {
107  assert(Region);
108  ProgramStateRef State = C.getState();
109  SVal S = State->getSVal(Region);
110  return S.getAsRegion();
111 }
112 
113 /// Checks whether a region is the part of an array.
114 /// In case there is a derived to base cast above the array element, the
115 /// Polymorphic output value is set to true. AKind output value is set to the
116 /// allocation kind of the inspected region.
117 const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
118  bool &Polymorphic,
119  AllocKind &AKind,
120  CheckerContext &C) const {
121  assert(Region);
122  while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
123  Region = BaseRegion->getSuperRegion();
124  Polymorphic = true;
125  }
126  if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
127  Region = ElemRegion->getSuperRegion();
128  }
129 
130  ProgramStateRef State = C.getState();
131  if (const AllocKind *Kind = State->get<RegionState>(Region)) {
132  AKind = *Kind;
133  if (*Kind == AllocKind::Array)
134  return Region;
135  else
136  return nullptr;
137  }
138  // When the region is symbolic and we do not have any information about it,
139  // assume that this is an array to avoid false positives.
140  if (isa<SymbolicRegion>(Region))
141  return Region;
142 
143  // No AllocKind stored and not symbolic, assume that it points to a single
144  // object.
145  return nullptr;
146 }
147 
148 void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
149  CheckerContext &C,
150  bool PointedNeeded) const {
151  SourceRange SR = E->getSourceRange();
152  if (SR.isInvalid())
153  return;
154 
155  ProgramStateRef State = C.getState();
156  const MemRegion *Region = C.getSVal(E).getAsRegion();
157  if (!Region)
158  return;
159  if (PointedNeeded)
160  Region = getPointedRegion(Region, C);
161  if (!Region)
162  return;
163 
164  bool IsPolymorphic = false;
166  if (const MemRegion *ArrayRegion =
167  getArrayRegion(Region, IsPolymorphic, Kind, C)) {
168  if (!IsPolymorphic)
169  return;
170  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
171  if (!BT_polyArray)
172  BT_polyArray.reset(new BuiltinBug(
173  this, "Dangerous pointer arithmetic",
174  "Pointer arithmetic on a pointer to base class is dangerous "
175  "because derived and base class may have different size."));
176  auto R = std::make_unique<PathSensitiveBugReport>(
177  *BT_polyArray, BT_polyArray->getDescription(), N);
178  R->addRange(E->getSourceRange());
179  R->markInteresting(ArrayRegion);
180  C.emitReport(std::move(R));
181  }
182  return;
183  }
184 
185  if (Kind == AllocKind::Reinterpreted)
186  return;
187 
188  // We might not have enough information about symbolic regions.
189  if (Kind != AllocKind::SingleObject &&
190  Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
191  return;
192 
193  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
194  if (!BT_pointerArith)
195  BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
196  "Pointer arithmetic on non-array "
197  "variables relies on memory layout, "
198  "which is dangerous."));
199  auto R = std::make_unique<PathSensitiveBugReport>(
200  *BT_pointerArith, BT_pointerArith->getDescription(), N);
201  R->addRange(SR);
202  R->markInteresting(Region);
203  C.emitReport(std::move(R));
204  }
205 }
206 
207 void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
208  if (!AllocFunctions.empty())
209  return;
210  AllocFunctions.insert(&C.Idents.get("alloca"));
211  AllocFunctions.insert(&C.Idents.get("malloc"));
212  AllocFunctions.insert(&C.Idents.get("realloc"));
213  AllocFunctions.insert(&C.Idents.get("calloc"));
214  AllocFunctions.insert(&C.Idents.get("valloc"));
215 }
216 
217 void PointerArithChecker::checkPostStmt(const CallExpr *CE,
218  CheckerContext &C) const {
219  ProgramStateRef State = C.getState();
220  const FunctionDecl *FD = C.getCalleeDecl(CE);
221  if (!FD)
222  return;
223  IdentifierInfo *FunI = FD->getIdentifier();
224  initAllocIdentifiers(C.getASTContext());
225  if (AllocFunctions.count(FunI) == 0)
226  return;
227 
228  SVal SV = C.getSVal(CE);
229  const MemRegion *Region = SV.getAsRegion();
230  if (!Region)
231  return;
232  // Assume that C allocation functions allocate arrays to avoid false
233  // positives.
234  // TODO: Add heuristics to distinguish alloc calls that allocates single
235  // objecs.
236  State = State->set<RegionState>(Region, AllocKind::Array);
237  C.addTransition(State);
238 }
239 
240 void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
241  CheckerContext &C) const {
242  const FunctionDecl *FD = NE->getOperatorNew();
243  if (!FD)
244  return;
245 
246  AllocKind Kind = getKindOfNewOp(NE, FD);
247 
248  ProgramStateRef State = C.getState();
249  SVal AllocedVal = C.getSVal(NE);
250  const MemRegion *Region = AllocedVal.getAsRegion();
251  if (!Region)
252  return;
253  State = State->set<RegionState>(Region, Kind);
254  C.addTransition(State);
255 }
256 
257 void PointerArithChecker::checkPostStmt(const CastExpr *CE,
258  CheckerContext &C) const {
259  if (CE->getCastKind() != CastKind::CK_BitCast)
260  return;
261 
262  const Expr *CastedExpr = CE->getSubExpr();
263  ProgramStateRef State = C.getState();
264  SVal CastedVal = C.getSVal(CastedExpr);
265 
266  const MemRegion *Region = CastedVal.getAsRegion();
267  if (!Region)
268  return;
269 
270  // Suppress reinterpret casted hits.
271  State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
272  C.addTransition(State);
273 }
274 
275 void PointerArithChecker::checkPreStmt(const CastExpr *CE,
276  CheckerContext &C) const {
277  if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
278  return;
279 
280  const Expr *CastedExpr = CE->getSubExpr();
281  ProgramStateRef State = C.getState();
282  SVal CastedVal = C.getSVal(CastedExpr);
283 
284  const MemRegion *Region = CastedVal.getAsRegion();
285  if (!Region)
286  return;
287 
288  if (const AllocKind *Kind = State->get<RegionState>(Region)) {
289  if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
290  return;
291  }
292  State = State->set<RegionState>(Region, AllocKind::Array);
293  C.addTransition(State);
294 }
295 
296 void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
297  CheckerContext &C) const {
298  if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
299  return;
300  reportPointerArithMisuse(UOp->getSubExpr(), C, true);
301 }
302 
303 void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
304  CheckerContext &C) const {
305  SVal Idx = C.getSVal(SubsExpr->getIdx());
306 
307  // Indexing with 0 is OK.
308  if (Idx.isZeroConstant())
309  return;
310 
311  // Indexing vector-type expressions is also OK.
312  if (SubsExpr->getBase()->getType()->isVectorType())
313  return;
314  reportPointerArithMisuse(SubsExpr->getBase(), C);
315 }
316 
317 void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
318  CheckerContext &C) const {
319  BinaryOperatorKind OpKind = BOp->getOpcode();
320  if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
321  return;
322 
323  const Expr *Lhs = BOp->getLHS();
324  const Expr *Rhs = BOp->getRHS();
325  ProgramStateRef State = C.getState();
326 
327  if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
328  SVal RHSVal = C.getSVal(Rhs);
329  if (State->isNull(RHSVal).isConstrainedTrue())
330  return;
331  reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
332  }
333  // The int += ptr; case is not valid C++.
334  if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
335  SVal LHSVal = C.getSVal(Lhs);
336  if (State->isNull(LHSVal).isConstrainedTrue())
337  return;
338  reportPointerArithMisuse(Rhs, C);
339  }
340 }
341 
342 void ento::registerPointerArithChecker(CheckerManager &mgr) {
343  mgr.registerChecker<PointerArithChecker>();
344 }
345 
346 bool ento::shouldRegisterPointerArithChecker(const LangOptions &LO) {
347  return true;
348 }
Represents a function declaration or definition.
Definition: Decl.h:1784
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Definition: Dominators.h:30
FunctionDecl * getOperatorNew() const
Definition: ExprCXX.h:2218
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Opcode getOpcode() const
Definition: Expr.h:3444
Defines the clang::Expr interface and subclasses for C++ expressions.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:269
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:160
LineState State
static void Profile(AllocKind X, FoldingSetNodeID &ID)
static bool isIncrementDecrementOp(Opcode Op)
Definition: Expr.h:2094
Expr * getSubExpr()
Definition: Expr.h:3177
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
IdentifierTable & Idents
Definition: ASTContext.h:579
BinaryOperatorKind
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3409
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3125
This represents one expression.
Definition: Expr.h:108
bool isVariadic() const
Whether this function is variadic.
Definition: Decl.cpp:2807
QualType getType() const
Definition: Expr.h:137
bool isInvalid() const
UnaryOperator - This represents the unary-expression&#39;s (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
Definition: Expr.h:2021
Kind
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Expr * getSubExpr() const
Definition: Expr.h:2051
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
CastKind getCastKind() const
Definition: Expr.h:3171
Represents a new-expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)"...
Definition: ExprCXX.h:2100
bool isArray() const
Definition: ExprCXX.h:2223
bool isVectorType() const
Definition: Type.h:6483
Expr * getLHS() const
Definition: Expr.h:3449
Dataflow Directional Tag Classes.
bool NE(InterpState &S, CodePtr OpPC)
Definition: Interp.h:226
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2437
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:6684
static bool isAdditiveOp(Opcode Opc)
Definition: Expr.h:3485
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:14652
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:262
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2521
Expr * getRHS() const
Definition: Expr.h:3451
bool isPointerType() const
Definition: Type.h:6391
A trivial tuple used to represent a source range.
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3178