Coverage Report

Created: 2017-10-03 07:32

/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
Line
Count
Source (jump to first uncovered line)
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
2.70k
  static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
38
2.70k
    ID.AddInteger(static_cast<int>(X));
39
2.70k
  }
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
48.1k
                                           CheckerContext &C) const {
79
48.1k
  // TODO: intentional leak. Some information is garbage collected too early,
80
48.1k
  // see http://reviews.llvm.org/D14203 for further information.
81
48.1k
  /*ProgramStateRef State = C.getState();
82
48.1k
  RegionStateTy RegionStates = State->get<RegionState>();
83
48.1k
  for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
84
48.1k
       I != E; ++I) {
85
48.1k
    if (!SR.isLiveRegion(I->first))
86
48.1k
      State = State->remove<RegionState>(I->first);
87
48.1k
  }
88
48.1k
  C.addTransition(State);*/
89
48.1k
}
90
91
AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
92
9
                                              const FunctionDecl *FD) const {
93
9
  // This checker try not to assume anything about placement and overloaded
94
9
  // new to avoid false positives.
95
9
  if (isa<CXXMethodDecl>(FD))
96
0
    return AllocKind::Unknown;
97
9
  
if (9
FD->getNumParams() != 1 || 9
FD->isVariadic()9
)
98
0
    return AllocKind::Unknown;
99
9
  
if (9
NE->isArray()9
)
100
4
    return AllocKind::Array;
101
5
102
5
  return AllocKind::SingleObject;
103
5
}
104
105
const MemRegion *
106
PointerArithChecker::getPointedRegion(const MemRegion *Region,
107
116
                                      CheckerContext &C) const {
108
116
  assert(Region);
109
116
  ProgramStateRef State = C.getState();
110
116
  SVal S = State->getSVal(Region);
111
116
  return S.getAsRegion();
112
116
}
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
15.8k
                                                     CheckerContext &C) const {
122
15.8k
  assert(Region);
123
15.8k
  while (
Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind15.8k
) {
124
1
    Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion();
125
1
    Polymorphic = true;
126
1
  }
127
15.8k
  if (
Region->getKind() == MemRegion::Kind::ElementRegionKind15.8k
) {
128
7.96k
    Region = Region->getAs<ElementRegion>()->getSuperRegion();
129
7.96k
  }
130
15.8k
131
15.8k
  ProgramStateRef State = C.getState();
132
15.8k
  if (const AllocKind *
Kind15.8k
= State->get<RegionState>(Region)) {
133
411
    AKind = *Kind;
134
411
    if (*Kind == AllocKind::Array)
135
243
      return Region;
136
411
    else
137
168
      return nullptr;
138
15.3k
  }
139
15.3k
  // When the region is symbolic and we do not have any information about it,
140
15.3k
  // assume that this is an array to avoid false positives.
141
15.3k
  
if (15.3k
Region->getKind() == MemRegion::Kind::SymbolicRegionKind15.3k
)
142
15.3k
    return Region;
143
8
144
8
  // No AllocKind stored and not symbolic, assume that it points to a single
145
8
  // object.
146
8
  return nullptr;
147
8
}
148
149
void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
150
                                                   CheckerContext &C,
151
15.8k
                                                   bool PointedNeeded) const {
152
15.8k
  SourceRange SR = E->getSourceRange();
153
15.8k
  if (SR.isInvalid())
154
0
    return;
155
15.8k
156
15.8k
  ProgramStateRef State = C.getState();
157
15.8k
  const MemRegion *Region =
158
15.8k
      State->getSVal(E, C.getLocationContext()).getAsRegion();
159
15.8k
  if (!Region)
160
6
    return;
161
15.8k
  
if (15.8k
PointedNeeded15.8k
)
162
116
    Region = getPointedRegion(Region, C);
163
15.8k
  if (!Region)
164
0
    return;
165
15.8k
166
15.8k
  bool IsPolymorphic = false;
167
15.8k
  AllocKind Kind = AllocKind::Unknown;
168
15.8k
  if (const MemRegion *ArrayRegion =
169
15.6k
          getArrayRegion(Region, IsPolymorphic, Kind, C)) {
170
15.6k
    if (!IsPolymorphic)
171
15.6k
      return;
172
1
    
if (ExplodedNode *1
N1
= C.generateNonFatalErrorNode()) {
173
1
      if (!BT_polyArray)
174
1
        BT_polyArray.reset(new BuiltinBug(
175
1
            this, "Dangerous pointer arithmetic",
176
1
            "Pointer arithmetic on a pointer to base class is dangerous "
177
1
            "because derived and base class may have different size."));
178
1
      auto R = llvm::make_unique<BugReport>(*BT_polyArray,
179
1
                                            BT_polyArray->getDescription(), N);
180
1
      R->addRange(E->getSourceRange());
181
1
      R->markInteresting(ArrayRegion);
182
1
      C.emitReport(std::move(R));
183
1
    }
184
15.6k
    return;
185
15.6k
  }
186
176
187
176
  
if (176
Kind == AllocKind::Reinterpreted176
)
188
166
    return;
189
10
190
10
  // We might not have enough information about symbolic regions.
191
10
  
if (10
Kind != AllocKind::SingleObject &&
192
8
      Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
193
0
    return;
194
10
195
10
  
if (ExplodedNode *10
N10
= C.generateNonFatalErrorNode()) {
196
10
    if (!BT_pointerArith)
197
4
      BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
198
4
                                           "Pointer arithmetic on non-array "
199
4
                                           "variables relies on memory layout, "
200
4
                                           "which is dangerous."));
201
10
    auto R = llvm::make_unique<BugReport>(*BT_pointerArith,
202
10
                                          BT_pointerArith->getDescription(), N);
203
10
    R->addRange(SR);
204
10
    R->markInteresting(Region);
205
10
    C.emitReport(std::move(R));
206
10
  }
207
15.8k
}
208
209
17.7k
void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
210
17.7k
  if (!AllocFunctions.empty())
211
17.6k
    return;
212
50
  AllocFunctions.insert(&C.Idents.get("alloca"));
213
50
  AllocFunctions.insert(&C.Idents.get("malloc"));
214
50
  AllocFunctions.insert(&C.Idents.get("realloc"));
215
50
  AllocFunctions.insert(&C.Idents.get("calloc"));
216
50
  AllocFunctions.insert(&C.Idents.get("valloc"));
217
50
}
218
219
void PointerArithChecker::checkPostStmt(const CallExpr *CE,
220
17.7k
                                        CheckerContext &C) const {
221
17.7k
  ProgramStateRef State = C.getState();
222
17.7k
  const FunctionDecl *FD = C.getCalleeDecl(CE);
223
17.7k
  if (!FD)
224
11
    return;
225
17.7k
  IdentifierInfo *FunI = FD->getIdentifier();
226
17.7k
  initAllocIdentifiers(C.getASTContext());
227
17.7k
  if (AllocFunctions.count(FunI) == 0)
228
17.7k
    return;
229
9
230
9
  SVal SV = State->getSVal(CE, C.getLocationContext());
231
9
  const MemRegion *Region = SV.getAsRegion();
232
9
  if (!Region)
233
0
    return;
234
9
  // Assume that C allocation functions allocate arrays to avoid false
235
9
  // positives.
236
9
  // TODO: Add heuristics to distinguish alloc calls that allocates single
237
9
  // objecs.
238
9
  State = State->set<RegionState>(Region, AllocKind::Array);
239
9
  C.addTransition(State);
240
9
}
241
242
void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
243
9
                                        CheckerContext &C) const {
244
9
  const FunctionDecl *FD = NE->getOperatorNew();
245
9
  if (!FD)
246
0
    return;
247
9
248
9
  AllocKind Kind = getKindOfNewOp(NE, FD);
249
9
250
9
  ProgramStateRef State = C.getState();
251
9
  SVal AllocedVal = State->getSVal(NE, C.getLocationContext());
252
9
  const MemRegion *Region = AllocedVal.getAsRegion();
253
9
  if (!Region)
254
0
    return;
255
9
  State = State->set<RegionState>(Region, Kind);
256
9
  C.addTransition(State);
257
9
}
258
259
void PointerArithChecker::checkPostStmt(const CastExpr *CE,
260
135k
                                        CheckerContext &C) const {
261
135k
  if (CE->getCastKind() != CastKind::CK_BitCast)
262
134k
    return;
263
804
264
804
  const Expr *CastedExpr = CE->getSubExpr();
265
804
  ProgramStateRef State = C.getState();
266
804
  SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
267
804
268
804
  const MemRegion *Region = CastedVal.getAsRegion();
269
804
  if (!Region)
270
66
    return;
271
738
272
738
  // Suppress reinterpret casted hits.
273
738
  State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
274
738
  C.addTransition(State);
275
738
}
276
277
void PointerArithChecker::checkPreStmt(const CastExpr *CE,
278
135k
                                       CheckerContext &C) const {
279
135k
  if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
280
135k
    return;
281
399
282
399
  const Expr *CastedExpr = CE->getSubExpr();
283
399
  ProgramStateRef State = C.getState();
284
399
  SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
285
399
286
399
  const MemRegion *Region = CastedVal.getAsRegion();
287
399
  if (!Region)
288
5
    return;
289
394
290
394
  
if (const AllocKind *394
Kind394
= State->get<RegionState>(Region)) {
291
206
    if (
*Kind == AllocKind::Array || 206
*Kind == AllocKind::Reinterpreted0
)
292
206
      return;
293
188
  }
294
188
  State = State->set<RegionState>(Region, AllocKind::Array);
295
188
  C.addTransition(State);
296
188
}
297
298
void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
299
17.1k
                                       CheckerContext &C) const {
300
17.1k
  if (
!UOp->isIncrementDecrementOp() || 17.1k
!UOp->getType()->isPointerType()609
)
301
17.0k
    return;
302
110
  reportPointerArithMisuse(UOp->getSubExpr(), C, true);
303
110
}
304
305
void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
306
376
                                       CheckerContext &C) const {
307
376
  ProgramStateRef State = C.getState();
308
376
  SVal Idx = State->getSVal(SubsExpr->getIdx(), C.getLocationContext());
309
376
310
376
  // Indexing with 0 is OK.
311
376
  if (Idx.isZeroConstant())
312
117
    return;
313
259
  reportPointerArithMisuse(SubsExpr->getBase(), C);
314
259
}
315
316
void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
317
50.2k
                                       CheckerContext &C) const {
318
50.2k
  BinaryOperatorKind OpKind = BOp->getOpcode();
319
50.2k
  if (
!BOp->isAdditiveOp() && 50.2k
OpKind != BO_AddAssign34.3k
&&
OpKind != BO_SubAssign34.3k
)
320
34.3k
    return;
321
15.8k
322
15.8k
  const Expr *Lhs = BOp->getLHS();
323
15.8k
  const Expr *Rhs = BOp->getRHS();
324
15.8k
  ProgramStateRef State = C.getState();
325
15.8k
326
15.8k
  if (
Rhs->getType()->isIntegerType() && 15.8k
Lhs->getType()->isPointerType()15.7k
) {
327
15.7k
    SVal RHSVal = State->getSVal(Rhs, C.getLocationContext());
328
15.7k
    if (State->isNull(RHSVal).isConstrainedTrue())
329
264
      return;
330
15.4k
    reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
331
15.4k
  }
332
15.8k
  // The int += ptr; case is not valid C++.
333
15.5k
  
if (15.5k
Lhs->getType()->isIntegerType() && 15.5k
Rhs->getType()->isPointerType()82
) {
334
3
    SVal LHSVal = State->getSVal(Lhs, C.getLocationContext());
335
3
    if (State->isNull(LHSVal).isConstrainedTrue())
336
1
      return;
337
2
    reportPointerArithMisuse(Rhs, C);
338
2
  }
339
50.2k
}
340
341
68
void ento::registerPointerArithChecker(CheckerManager &mgr) {
342
68
  mgr.registerChecker<PointerArithChecker>();
343
68
}