Coverage Report

Created: 2023-09-30 09:22

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