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