clang 18.0.0git
VLASizeChecker.cpp
Go to the documentation of this file.
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"
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/SmallString.h"
26#include "llvm/Support/raw_ostream.h"
27#include <optional>
28
29using namespace clang;
30using namespace ento;
31using namespace taint;
32
33namespace {
34class 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.
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
58public:
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
65ProgramStateRef 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;
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
150ProgramStateRef 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();
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
209void 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",
220
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
236void 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",
247
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
272void 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
319void 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(
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
344void ento::registerVLASizeChecker(CheckerManager &mgr) {
345 mgr.registerChecker<VLASizeChecker>();
346}
347
348bool ento::shouldRegisterVLASizeChecker(const CheckerManager &mgr) {
349 return true;
350}
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
CanQualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
const VariableArrayType * getAsVariableArrayType(QualType T) const
Definition: ASTContext.h:2731
QualType getElementType() const
Definition: Type.h:3157
CharUnits - This is an opaque type for sizes expressed in character units.
Definition: CharUnits.h:38
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition: CharUnits.h:185
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1493
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition: Stmt.h:1506
const Decl * getSingleDecl() const
Definition: Stmt.h:1508
This represents one expression.
Definition: Expr.h:110
QualType getType() const
Definition: Expr.h:142
A (possibly-)qualified type.
Definition: Type.h:736
QualType getCanonicalType() const
Definition: Type.h:6833
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:325
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition: Expr.h:2595
QualType getTypeOfArgument() const
Gets the argument type, or the type of the argument expression, whichever is appropriate.
Definition: Expr.h:2664
bool isArgumentType() const
Definition: Expr.h:2637
UnaryExprOrTypeTrait getKind() const
Definition: Expr.h:2627
QualType getType() const
Definition: Decl.h:715
Represents a variable declaration or definition.
Definition: Decl.h:916
Represents a C array with a specified size that is not an integer-constant-expression.
Definition: Type.h:3288
Expr * getSizeExpr() const
Definition: Type.h:3307
const llvm::APSInt & getMaxValue(const llvm::APSInt &v)
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
Definition: SValBuilder.cpp:62
virtual const llvm::APSInt * getKnownValue(ProgramStateRef state, SVal val)=0
Evaluates a given SVal.
BasicValueFactory & getBasicValueFactory()
Definition: SValBuilder.h:157
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
Definition: SValBuilder.h:286
virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy)=0
Create a new value which represents a binary expression with two non- location operands.
SVal evalCast(SVal V, QualType CastTy, QualType OriginalTy)
Cast a given SVal to another SVal using given QualType's.
QualType getConditionType() const
Definition: SValBuilder.h:149
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
bool isUndef() const
Definition: SVals.h:104
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:82
bool isUnknown() const
Definition: SVals.h:102
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
std::vector< SymbolRef > getTaintedSymbols(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx, TaintTagType Kind=TaintTagGeneric)
Returns the tainted Symbols for a given Statement and state.
Definition: Taint.cpp:169
bool isTainted(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx, TaintTagType Kind=TaintTagGeneric)
Check if the statement has a tainted value in the given state.
Definition: Taint.cpp:147
ProgramStateRef setDynamicExtent(ProgramStateRef State, const MemRegion *MR, DefinedOrUnknownSVal Extent, SValBuilder &SVB)
Set the dynamic extent Extent of the region MR.
bool Zero(InterpState &S, CodePtr OpPC)
Definition: Interp.h:1686
bool Mul(InterpState &S, CodePtr OpPC)
Definition: Interp.h:339
unsigned long uint64_t