Clang Project

clang_source_code/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17#include "clang/AST/CharUnits.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#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/Support/raw_ostream.h"
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
31  mutable std::unique_ptr<BugTypeBT;
32  enum VLASize_Kind { VLA_GarbageVLA_ZeroVLA_TaintedVLA_Negative };
33
34  void reportBug(VLASize_Kind Kindconst Expr *SizeEProgramStateRef State,
35                 CheckerContext &C,
36                 std::unique_ptr<BugReporterVisitorVisitor = nullptrconst;
37
38public:
39  void checkPreStmt(const DeclStmt *DSCheckerContext &Cconst;
40};
41// end anonymous namespace
42
43void VLASizeChecker::reportBug(
44    VLASize_Kind Kindconst Expr *SizeEProgramStateRef State,
45    CheckerContext &Cstd::unique_ptr<BugReporterVisitorVisitorconst {
46  // Generate an error node.
47  ExplodedNode *N = C.generateErrorNode(State);
48  if (!N)
49    return;
50
51  if (!BT)
52    BT.reset(new BuiltinBug(
53        this"Dangerous variable-length array (VLA) declaration"));
54
55  SmallString<256buf;
56  llvm::raw_svector_ostream os(buf);
57  os << "Declared variable-length array (VLA) ";
58  switch (Kind) {
59  case VLA_Garbage:
60    os << "uses a garbage value as its size";
61    break;
62  case VLA_Zero:
63    os << "has zero size";
64    break;
65  case VLA_Tainted:
66    os << "has tainted size";
67    break;
68  case VLA_Negative:
69    os << "has negative size";
70    break;
71  }
72
73  auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
74  report->addVisitor(std::move(Visitor));
75  report->addRange(SizeE->getSourceRange());
76  bugreporter::trackExpressionValue(N, SizeE, *report);
77  C.emitReport(std::move(report));
78}
79
80void VLASizeChecker::checkPreStmt(const DeclStmt *DSCheckerContext &Cconst {
81  if (!DS->isSingleDecl())
82    return;
83
84  const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
85  if (!VD)
86    return;
87
88  ASTContext &Ctx = C.getASTContext();
89  const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType());
90  if (!VLA)
91    return;
92
93  // FIXME: Handle multi-dimensional VLAs.
94  const Expr *SE = VLA->getSizeExpr();
95  ProgramStateRef state = C.getState();
96  SVal sizeV = C.getSVal(SE);
97
98  if (sizeV.isUndef()) {
99    reportBug(VLA_Garbage, SE, state, C);
100    return;
101  }
102
103  // See if the size value is known. It can't be undefined because we would have
104  // warned about that already.
105  if (sizeV.isUnknown())
106    return;
107
108  // Check if the size is tainted.
109  if (state->isTainted(sizeV)) {
110    reportBug(VLA_Tainted, SE, nullptr, C,
111              llvm::make_unique<TaintBugVisitor>(sizeV));
112    return;
113  }
114
115  // Check if the size is zero.
116  DefinedSVal sizeD = sizeV.castAs<DefinedSVal>();
117
118  ProgramStateRef stateNotZerostateZero;
119  std::tie(stateNotZero, stateZero) = state->assume(sizeD);
120
121  if (stateZero && !stateNotZero) {
122    reportBug(VLA_Zero, SE, stateZero, C);
123    return;
124  }
125
126  // From this point on, assume that the size is not zero.
127  state = stateNotZero;
128
129  // VLASizeChecker is responsible for defining the extent of the array being
130  // declared. We do this by multiplying the array length by the element size,
131  // then matching that with the array region's extent symbol.
132
133  // Check if the size is negative.
134  SValBuilder &svalBuilder = C.getSValBuilder();
135
136  QualType Ty = SE->getType();
137  DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty);
138
139  SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty);
140  if (Optional<DefinedSVal> LessThanZeroDVal =
141        LessThanZeroVal.getAs<DefinedSVal>()) {
142    ConstraintManager &CM = C.getConstraintManager();
143    ProgramStateRef StatePosStateNeg;
144
145    std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal);
146    if (StateNeg && !StatePos) {
147      reportBug(VLA_Negative, SE, state, C);
148      return;
149    }
150    state = StatePos;
151  }
152
153  // Convert the array length to size_t.
154  QualType SizeTy = Ctx.getSizeType();
155  NonLoc ArrayLength =
156      svalBuilder.evalCast(sizeDSizeTySE->getType()).castAs<NonLoc>();
157
158  // Get the element size.
159  CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType());
160  SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy);
161
162  // Multiply the array length by the element size.
163  SVal ArraySizeVal = svalBuilder.evalBinOpNN(
164      state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy);
165
166  // Finally, assume that the array's extent matches the given size.
167  const LocationContext *LC = C.getLocationContext();
168  DefinedOrUnknownSVal Extent =
169    state->getRegion(VD, LC)->getExtent(svalBuilder);
170  DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>();
171  DefinedOrUnknownSVal sizeIsKnown =
172    svalBuilder.evalEQ(state, Extent, ArraySize);
173  state = state->assume(sizeIsKnown, true);
174
175  // Assume should not fail at this point.
176  assert(state);
177
178  // Remember our assumptions!
179  C.addTransition(state);
180}
181
182void ento::registerVLASizeChecker(CheckerManager &mgr) {
183  mgr.registerChecker<VLASizeChecker>();
184}
185
186bool ento::shouldRegisterVLASizeChecker(const LangOptions &LO) {
187  return true;
188}
189