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