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