xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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 =
192       SVB.evalBinOp(State, BO_LT, SizeD, Zero, SVB.getConditionType());
193   if (std::optional<DefinedSVal> LessThanZeroDVal =
194           LessThanZeroVal.getAs<DefinedSVal>()) {
195     ConstraintManager &CM = C.getConstraintManager();
196     ProgramStateRef StatePos, StateNeg;
197 
198     std::tie(StateNeg, StatePos) = CM.assumeDual(State, *LessThanZeroDVal);
199     if (StateNeg && !StatePos) {
200       reportBug(VLA_Negative, SizeE, State, C);
201       return nullptr;
202     }
203     State = StatePos;
204   }
205 
206   return State;
207 }
208 
209 void VLASizeChecker::reportTaintBug(const Expr *SizeE, ProgramStateRef State,
210                                     CheckerContext &C, SVal TaintedSVal) const {
211   // Generate an error node.
212   ExplodedNode *N = C.generateErrorNode(State);
213   if (!N)
214     return;
215 
216   if (!TaintBT)
217     TaintBT.reset(
218         new BugType(this, "Dangerous variable-length array (VLA) declaration",
219                     categories::TaintedData));
220 
221   SmallString<256> buf;
222   llvm::raw_svector_ostream os(buf);
223   os << "Declared variable-length array (VLA) ";
224   os << "has tainted size";
225 
226   auto report = std::make_unique<PathSensitiveBugReport>(*TaintBT, os.str(), N);
227   report->addRange(SizeE->getSourceRange());
228   bugreporter::trackExpressionValue(N, SizeE, *report);
229   // The vla size may be a complex expression where multiple memory locations
230   // are tainted.
231   for (auto Sym : getTaintedSymbols(State, TaintedSVal))
232     report->markInteresting(Sym);
233   C.emitReport(std::move(report));
234 }
235 
236 void VLASizeChecker::reportBug(VLASize_Kind Kind, const Expr *SizeE,
237                                ProgramStateRef State, CheckerContext &C) const {
238   // Generate an error node.
239   ExplodedNode *N = C.generateErrorNode(State);
240   if (!N)
241     return;
242 
243   if (!BT)
244     BT.reset(new BugType(this,
245                          "Dangerous variable-length array (VLA) declaration",
246                          categories::LogicError));
247 
248   SmallString<256> buf;
249   llvm::raw_svector_ostream os(buf);
250   os << "Declared variable-length array (VLA) ";
251   switch (Kind) {
252   case VLA_Garbage:
253     os << "uses a garbage value as its size";
254     break;
255   case VLA_Zero:
256     os << "has zero size";
257     break;
258   case VLA_Negative:
259     os << "has negative size";
260     break;
261   case VLA_Overflow:
262     os << "has too large size";
263     break;
264   }
265 
266   auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
267   report->addRange(SizeE->getSourceRange());
268   bugreporter::trackExpressionValue(N, SizeE, *report);
269   C.emitReport(std::move(report));
270 }
271 
272 void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
273   if (!DS->isSingleDecl())
274     return;
275 
276   ASTContext &Ctx = C.getASTContext();
277   SValBuilder &SVB = C.getSValBuilder();
278   ProgramStateRef State = C.getState();
279   QualType TypeToCheck;
280 
281   const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
282 
283   if (VD)
284     TypeToCheck = VD->getType().getCanonicalType();
285   else if (const auto *TND = dyn_cast<TypedefNameDecl>(DS->getSingleDecl()))
286     TypeToCheck = TND->getUnderlyingType().getCanonicalType();
287   else
288     return;
289 
290   const VariableArrayType *VLA = Ctx.getAsVariableArrayType(TypeToCheck);
291   if (!VLA)
292     return;
293 
294   // Check the VLA sizes for validity.
295 
296   SVal ArraySize;
297 
298   State = checkVLA(C, State, VLA, ArraySize);
299   if (!State)
300     return;
301 
302   if (!isa<NonLoc>(ArraySize)) {
303     // Array size could not be determined but state may contain new assumptions.
304     C.addTransition(State);
305     return;
306   }
307 
308   // VLASizeChecker is responsible for defining the extent of the array.
309   if (VD) {
310     State =
311         setDynamicExtent(State, State->getRegion(VD, C.getLocationContext()),
312                          ArraySize.castAs<NonLoc>(), SVB);
313   }
314 
315   // Remember our assumptions!
316   C.addTransition(State);
317 }
318 
319 void VLASizeChecker::checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE,
320                                   CheckerContext &C) const {
321   // Want to check for sizeof.
322   if (UETTE->getKind() != UETT_SizeOf)
323     return;
324 
325   // Ensure a type argument.
326   if (!UETTE->isArgumentType())
327     return;
328 
329   const VariableArrayType *VLA = C.getASTContext().getAsVariableArrayType(
330       UETTE->getTypeOfArgument().getCanonicalType());
331   // Ensure that the type is a VLA.
332   if (!VLA)
333     return;
334 
335   ProgramStateRef State = C.getState();
336   SVal ArraySize;
337   State = checkVLA(C, State, VLA, ArraySize);
338   if (!State)
339     return;
340 
341   C.addTransition(State);
342 }
343 
344 void ento::registerVLASizeChecker(CheckerManager &mgr) {
345   mgr.registerChecker<VLASizeChecker>();
346 }
347 
348 bool ento::shouldRegisterVLASizeChecker(const CheckerManager &mgr) {
349   return true;
350 }
351