Coverage Report

Created: 2019-07-24 05:18

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