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 "Taint.h" 17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18 #include "clang/AST/CharUnits.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 "llvm/ADT/STLExtras.h" 24 #include "llvm/ADT/SmallString.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 using namespace clang; 28 using namespace ento; 29 using namespace taint; 30 31 namespace { 32 class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > { 33 mutable std::unique_ptr<BugType> BT; 34 enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative }; 35 36 void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, 37 CheckerContext &C, 38 std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; 39 40 public: 41 void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; 42 }; 43 } // end anonymous namespace 44 45 void VLASizeChecker::reportBug( 46 VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, 47 CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const { 48 // Generate an error node. 49 ExplodedNode *N = C.generateErrorNode(State); 50 if (!N) 51 return; 52 53 if (!BT) 54 BT.reset(new BuiltinBug( 55 this, "Dangerous variable-length array (VLA) declaration")); 56 57 SmallString<256> buf; 58 llvm::raw_svector_ostream os(buf); 59 os << "Declared variable-length array (VLA) "; 60 switch (Kind) { 61 case VLA_Garbage: 62 os << "uses a garbage value as its size"; 63 break; 64 case VLA_Zero: 65 os << "has zero size"; 66 break; 67 case VLA_Tainted: 68 os << "has tainted size"; 69 break; 70 case VLA_Negative: 71 os << "has negative size"; 72 break; 73 } 74 75 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); 76 report->addVisitor(std::move(Visitor)); 77 report->addRange(SizeE->getSourceRange()); 78 bugreporter::trackExpressionValue(N, SizeE, *report); 79 C.emitReport(std::move(report)); 80 } 81 82 void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { 83 if (!DS->isSingleDecl()) 84 return; 85 86 const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); 87 if (!VD) 88 return; 89 90 ASTContext &Ctx = C.getASTContext(); 91 const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); 92 if (!VLA) 93 return; 94 95 // FIXME: Handle multi-dimensional VLAs. 96 const Expr *SE = VLA->getSizeExpr(); 97 ProgramStateRef state = C.getState(); 98 SVal sizeV = C.getSVal(SE); 99 100 if (sizeV.isUndef()) { 101 reportBug(VLA_Garbage, SE, state, C); 102 return; 103 } 104 105 // See if the size value is known. It can't be undefined because we would have 106 // warned about that already. 107 if (sizeV.isUnknown()) 108 return; 109 110 // Check if the size is tainted. 111 if (isTainted(state, sizeV)) { 112 reportBug(VLA_Tainted, SE, nullptr, C, 113 std::make_unique<TaintBugVisitor>(sizeV)); 114 return; 115 } 116 117 // Check if the size is zero. 118 DefinedSVal sizeD = sizeV.castAs<DefinedSVal>(); 119 120 ProgramStateRef stateNotZero, stateZero; 121 std::tie(stateNotZero, stateZero) = state->assume(sizeD); 122 123 if (stateZero && !stateNotZero) { 124 reportBug(VLA_Zero, SE, stateZero, C); 125 return; 126 } 127 128 // From this point on, assume that the size is not zero. 129 state = stateNotZero; 130 131 // VLASizeChecker is responsible for defining the extent of the array being 132 // declared. We do this by multiplying the array length by the element size, 133 // then matching that with the array region's extent symbol. 134 135 // Check if the size is negative. 136 SValBuilder &svalBuilder = C.getSValBuilder(); 137 138 QualType Ty = SE->getType(); 139 DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty); 140 141 SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty); 142 if (Optional<DefinedSVal> LessThanZeroDVal = 143 LessThanZeroVal.getAs<DefinedSVal>()) { 144 ConstraintManager &CM = C.getConstraintManager(); 145 ProgramStateRef StatePos, StateNeg; 146 147 std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal); 148 if (StateNeg && !StatePos) { 149 reportBug(VLA_Negative, SE, state, C); 150 return; 151 } 152 state = StatePos; 153 } 154 155 // Convert the array length to size_t. 156 QualType SizeTy = Ctx.getSizeType(); 157 NonLoc ArrayLength = 158 svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>(); 159 160 // Get the element size. 161 CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); 162 SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); 163 164 // Multiply the array length by the element size. 165 SVal ArraySizeVal = svalBuilder.evalBinOpNN( 166 state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy); 167 168 // Finally, assume that the array's extent matches the given size. 169 const LocationContext *LC = C.getLocationContext(); 170 DefinedOrUnknownSVal Extent = 171 state->getRegion(VD, LC)->getExtent(svalBuilder); 172 DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>(); 173 DefinedOrUnknownSVal sizeIsKnown = 174 svalBuilder.evalEQ(state, Extent, ArraySize); 175 state = state->assume(sizeIsKnown, true); 176 177 // Assume should not fail at this point. 178 assert(state); 179 180 // Remember our assumptions! 181 C.addTransition(state); 182 } 183 184 void ento::registerVLASizeChecker(CheckerManager &mgr) { 185 mgr.registerChecker<VLASizeChecker>(); 186 } 187 188 bool ento::shouldRegisterVLASizeChecker(const LangOptions &LO) { 189 return true; 190 } 191