Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//=== VLASizeChecker.cpp - Undefined dereference 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 defines VLASizeChecker, a builtin check in ExprEngine that
10
// performs checks for declaration of VLA of undefined or zero size.
11
// In addition, VLASizeChecker is responsible for defining the extent
12
// of the MemRegion that represents a VLA.
13
//
14
//===----------------------------------------------------------------------===//
15
16
#include "Taint.h"
17
#include "clang/AST/CharUnits.h"
18
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20
#include "clang/StaticAnalyzer/Core/Checker.h"
21
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
24
#include "llvm/ADT/STLExtras.h"
25
#include "llvm/ADT/SmallString.h"
26
#include "llvm/Support/raw_ostream.h"
27
28
using namespace clang;
29
using namespace ento;
30
using namespace taint;
31
32
namespace {
33
class VLASizeChecker
34
    : public Checker<check::PreStmt<DeclStmt>,
35
                     check::PreStmt<UnaryExprOrTypeTraitExpr>> {
36
  mutable std::unique_ptr<BugType> BT;
37
  enum VLASize_Kind {
38
    VLA_Garbage,
39
    VLA_Zero,
40
    VLA_Tainted,
41
    VLA_Negative,
42
    VLA_Overflow
43
  };
44
45
  /// Check a VLA for validity.
46
  /// Every dimension of the array and the total size is checked for validity.
47
  /// Returns null or a new state where the size is validated.
48
  /// 'ArraySize' will contain SVal that refers to the total size (in char)
49
  /// of the array.
50
  ProgramStateRef checkVLA(CheckerContext &C, ProgramStateRef State,
51
                           const VariableArrayType *VLA, SVal &ArraySize) const;
52
  /// Check a single VLA index size expression for validity.
53
  ProgramStateRef checkVLAIndexSize(CheckerContext &C, ProgramStateRef State,
54
                                    const Expr *SizeE) const;
55
56
  void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
57
                 CheckerContext &C,
58
                 std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
59
60
public:
61
  void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
62
  void checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE,
63
                    CheckerContext &C) const;
64
};
65
} // end anonymous namespace
66
67
ProgramStateRef VLASizeChecker::checkVLA(CheckerContext &C,
68
                                         ProgramStateRef State,
69
                                         const VariableArrayType *VLA,
70
61
                                         SVal &ArraySize) const {
71
61
  assert(VLA && "Function should be called with non-null VLA argument.");
72
73
0
  const VariableArrayType *VLALast = nullptr;
74
61
  llvm::SmallVector<const Expr *, 2> VLASizes;
75
76
  // Walk over the VLAs for every dimension until a non-VLA is found.
77
  // There is a VariableArrayType for every dimension (fixed or variable) until
78
  // the most inner array that is variably modified.
79
  // Dimension sizes are collected into 'VLASizes'. 'VLALast' is set to the
80
  // innermost VLA that was encountered.
81
  // In "int vla[x][2][y][3]" this will be the array for index "y" (with type
82
  // int[3]). 'VLASizes' contains 'x', '2', and 'y'.
83
122
  while (VLA) {
84
78
    const Expr *SizeE = VLA->getSizeExpr();
85
78
    State = checkVLAIndexSize(C, State, SizeE);
86
78
    if (!State)
87
17
      return nullptr;
88
61
    VLASizes.push_back(SizeE);
89
61
    VLALast = VLA;
90
61
    VLA = C.getASTContext().getAsVariableArrayType(VLA->getElementType());
91
61
  };
92
44
  assert(VLALast &&
93
44
         "Array should have at least one variably-modified dimension.");
94
95
0
  ASTContext &Ctx = C.getASTContext();
96
44
  SValBuilder &SVB = C.getSValBuilder();
97
44
  CanQualType SizeTy = Ctx.getSizeType();
98
44
  uint64_t SizeMax =
99
44
      SVB.getBasicValueFactory().getMaxValue(SizeTy).getZExtValue();
100
101
  // Get the element size.
102
44
  CharUnits EleSize = Ctx.getTypeSizeInChars(VLALast->getElementType());
103
44
  NonLoc ArrSize =
104
44
      SVB.makeIntVal(EleSize.getQuantity(), SizeTy).castAs<NonLoc>();
105
106
  // Try to calculate the known real size of the array in KnownSize.
107
44
  uint64_t KnownSize = 0;
108
44
  if (const llvm::APSInt *KV = SVB.getKnownValue(State, ArrSize))
109
44
    KnownSize = KV->getZExtValue();
110
111
59
  for (const Expr *SizeE : VLASizes) {
112
59
    auto SizeD = C.getSVal(SizeE).castAs<DefinedSVal>();
113
    // Convert the array length to size_t.
114
59
    NonLoc IndexLength =
115
59
        SVB.evalCast(SizeD, SizeTy, SizeE->getType()).castAs<NonLoc>();
116
    // Multiply the array length by the element size.
117
59
    SVal Mul = SVB.evalBinOpNN(State, BO_Mul, ArrSize, IndexLength, SizeTy);
118
59
    if (auto MulNonLoc = Mul.getAs<NonLoc>())
119
59
      ArrSize = *MulNonLoc;
120
0
    else
121
      // Extent could not be determined.
122
0
      return State;
123
124
59
    if (const llvm::APSInt *IndexLVal = SVB.getKnownValue(State, IndexLength)) {
125
      // Check if the array size will overflow.
126
      // Size overflow check does not work with symbolic expressions because a
127
      // overflow situation can not be detected easily.
128
42
      uint64_t IndexL = IndexLVal->getZExtValue();
129
      // FIXME: See https://reviews.llvm.org/D80903 for discussion of
130
      // some difference in assume and getKnownValue that leads to
131
      // unexpected behavior. Just bail on IndexL == 0 at this point.
132
42
      if (IndexL == 0)
133
0
        return nullptr;
134
135
42
      if (KnownSize <= SizeMax / IndexL) {
136
40
        KnownSize *= IndexL;
137
40
      } else {
138
        // Array size does not fit into size_t.
139
2
        reportBug(VLA_Overflow, SizeE, State, C);
140
2
        return nullptr;
141
2
      }
142
42
    } else {
143
17
      KnownSize = 0;
144
17
    }
145
59
  }
146
147
42
  ArraySize = ArrSize;
148
149
42
  return State;
150
44
}
151
152
ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C,
153
                                                  ProgramStateRef State,
154
78
                                                  const Expr *SizeE) const {
155
78
  SVal SizeV = C.getSVal(SizeE);
156
157
78
  if (SizeV.isUndef()) {
158
1
    reportBug(VLA_Garbage, SizeE, State, C);
159
1
    return nullptr;
160
1
  }
161
162
  // See if the size value is known. It can't be undefined because we would have
163
  // warned about that already.
164
77
  if (SizeV.isUnknown())
165
3
    return nullptr;
166
167
  // Check if the size is tainted.
168
74
  if (isTainted(State, SizeV)) {
169
3
    reportBug(VLA_Tainted, SizeE, nullptr, C,
170
3
              std::make_unique<TaintBugVisitor>(SizeV));
171
3
    return nullptr;
172
3
  }
173
174
  // Check if the size is zero.
175
71
  DefinedSVal SizeD = SizeV.castAs<DefinedSVal>();
176
177
71
  ProgramStateRef StateNotZero, StateZero;
178
71
  std::tie(StateNotZero, StateZero) = State->assume(SizeD);
179
180
71
  if (StateZero && 
!StateNotZero15
) {
181
3
    reportBug(VLA_Zero, SizeE, StateZero, C);
182
3
    return nullptr;
183
3
  }
184
185
  // From this point on, assume that the size is not zero.
186
68
  State = StateNotZero;
187
188
  // Check if the size is negative.
189
68
  SValBuilder &SVB = C.getSValBuilder();
190
191
68
  QualType SizeTy = SizeE->getType();
192
68
  DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy);
193
194
68
  SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy);
195
68
  if (Optional<DefinedSVal> LessThanZeroDVal =
196
68
          LessThanZeroVal.getAs<DefinedSVal>()) {
197
68
    ConstraintManager &CM = C.getConstraintManager();
198
68
    ProgramStateRef StatePos, StateNeg;
199
200
68
    std::tie(StateNeg, StatePos) = CM.assumeDual(State, *LessThanZeroDVal);
201
68
    if (StateNeg && 
!StatePos19
) {
202
7
      reportBug(VLA_Negative, SizeE, State, C);
203
7
      return nullptr;
204
7
    }
205
61
    State = StatePos;
206
61
  }
207
208
61
  return State;
209
68
}
210
211
void VLASizeChecker::reportBug(
212
    VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
213
16
    CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
214
  // Generate an error node.
215
16
  ExplodedNode *N = C.generateErrorNode(State);
216
16
  if (!N)
217
0
    return;
218
219
16
  if (!BT)
220
5
    BT.reset(new BuiltinBug(
221
5
        this, "Dangerous variable-length array (VLA) declaration"));
222
223
16
  SmallString<256> buf;
224
16
  llvm::raw_svector_ostream os(buf);
225
16
  os << "Declared variable-length array (VLA) ";
226
16
  switch (Kind) {
227
1
  case VLA_Garbage:
228
1
    os << "uses a garbage value as its size";
229
1
    break;
230
3
  case VLA_Zero:
231
3
    os << "has zero size";
232
3
    break;
233
3
  case VLA_Tainted:
234
3
    os << "has tainted size";
235
3
    break;
236
7
  case VLA_Negative:
237
7
    os << "has negative size";
238
7
    break;
239
2
  case VLA_Overflow:
240
2
    os << "has too large size";
241
2
    break;
242
16
  }
243
244
16
  auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
245
16
  report->addVisitor(std::move(Visitor));
246
16
  report->addRange(SizeE->getSourceRange());
247
16
  bugreporter::trackExpressionValue(N, SizeE, *report);
248
16
  C.emitReport(std::move(report));
249
16
}
250
251
30.8k
void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
252
30.8k
  if (!DS->isSingleDecl())
253
0
    return;
254
255
30.8k
  ASTContext &Ctx = C.getASTContext();
256
30.8k
  SValBuilder &SVB = C.getSValBuilder();
257
30.8k
  ProgramStateRef State = C.getState();
258
30.8k
  QualType TypeToCheck;
259
260
30.8k
  const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
261
262
30.8k
  if (VD)
263
30.8k
    TypeToCheck = VD->getType().getCanonicalType();
264
5
  else if (const auto *TND = dyn_cast<TypedefNameDecl>(DS->getSingleDecl()))
265
5
    TypeToCheck = TND->getUnderlyingType().getCanonicalType();
266
0
  else
267
0
    return;
268
269
30.8k
  const VariableArrayType *VLA = Ctx.getAsVariableArrayType(TypeToCheck);
270
30.8k
  if (!VLA)
271
30.8k
    return;
272
273
  // Check the VLA sizes for validity.
274
275
55
  SVal ArraySize;
276
277
55
  State = checkVLA(C, State, VLA, ArraySize);
278
55
  if (!State)
279
17
    return;
280
281
38
  auto ArraySizeNL = ArraySize.getAs<NonLoc>();
282
38
  if (!ArraySizeNL) {
283
    // Array size could not be determined but state may contain new assumptions.
284
0
    C.addTransition(State);
285
0
    return;
286
0
  }
287
288
  // VLASizeChecker is responsible for defining the extent of the array.
289
38
  if (VD) {
290
35
    State =
291
35
        setDynamicExtent(State, State->getRegion(VD, C.getLocationContext()),
292
35
                         ArraySize.castAs<DefinedOrUnknownSVal>(), SVB);
293
35
  }
294
295
  // Remember our assumptions!
296
38
  C.addTransition(State);
297
38
}
298
299
void VLASizeChecker::checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE,
300
929
                                  CheckerContext &C) const {
301
  // Want to check for sizeof.
302
929
  if (UETTE->getKind() != UETT_SizeOf)
303
3
    return;
304
305
  // Ensure a type argument.
306
926
  if (!UETTE->isArgumentType())
307
365
    return;
308
309
561
  const VariableArrayType *VLA = C.getASTContext().getAsVariableArrayType(
310
561
      UETTE->getTypeOfArgument().getCanonicalType());
311
  // Ensure that the type is a VLA.
312
561
  if (!VLA)
313
555
    return;
314
315
6
  ProgramStateRef State = C.getState();
316
6
  SVal ArraySize;
317
6
  State = checkVLA(C, State, VLA, ArraySize);
318
6
  if (!State)
319
2
    return;
320
321
4
  C.addTransition(State);
322
4
}
323
324
1.15k
void ento::registerVLASizeChecker(CheckerManager &mgr) {
325
1.15k
  mgr.registerChecker<VLASizeChecker>();
326
1.15k
}
327
328
2.31k
bool ento::shouldRegisterVLASizeChecker(const CheckerManager &mgr) {
329
2.31k
  return true;
330
2.31k
}