diff --git a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -30,10 +30,16 @@ using namespace taint; namespace { -class VLASizeChecker : public Checker< check::PreStmt > { +class VLASizeChecker + : public Checker, + check::PreStmt> { mutable std::unique_ptr BT; enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative }; + ProgramStateRef checkVLA(CheckerContext &C, ProgramStateRef State, + const VariableArrayType *VLA, + const VariableArrayType *&VLALast, + llvm::SmallVector &VLASizes) const; ProgramStateRef checkVLASize(CheckerContext &C, ProgramStateRef State, const Expr *SizeE) const; @@ -43,9 +49,39 @@ public: void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE, + CheckerContext &C) const; }; } // end anonymous namespace +ProgramStateRef +VLASizeChecker::checkVLA(CheckerContext &C, ProgramStateRef State, + const VariableArrayType *VLA, + const VariableArrayType *&VLALast, + llvm::SmallVector &VLASizes) const { + assert(VLA && "Function should be called with non-null VLA argument."); + + VLALast = nullptr; + // Walk over the VLAs for every dimension until a non-VLA is found. + // Collect the sizes in VLASizes, put the most inner VLA to `VLALast`. + // In "vla[x][2][y][3]" this will be the array for index "y". + // There is a VariableArrayType for every dimension (here "x", "2", "y") + // until a non-vla is found. + while (VLA) { + const Expr *SizeE = VLA->getSizeExpr(); + State = checkVLASize(C, State, SizeE); + if (!State) + return nullptr; + VLASizes.push_back(SizeE); + VLALast = VLA; + VLA = C.getASTContext().getAsVariableArrayType(VLA->getElementType()); + }; + assert(VLALast && + "Array should have at least one variably-modified dimension."); + + return State; +} + ProgramStateRef VLASizeChecker::checkVLASize(CheckerContext &C, ProgramStateRef State, const Expr *SizeE) const { @@ -146,36 +182,34 @@ if (!DS->isSingleDecl()) return; - const VarDecl *VD = dyn_cast(DS->getSingleDecl()); - if (!VD) - return; - ASTContext &Ctx = C.getASTContext(); SValBuilder &SVB = C.getSValBuilder(); ProgramStateRef State = C.getState(); + QualType TypeToCheck; - const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); + const VarDecl *VD = dyn_cast(DS->getSingleDecl()); + + if (VD) + TypeToCheck = VD->getType().getCanonicalType(); + else if (const auto *TND = dyn_cast(DS->getSingleDecl())) + TypeToCheck = TND->getUnderlyingType().getCanonicalType(); + else + return; + + const VariableArrayType *VLA = Ctx.getAsVariableArrayType(TypeToCheck); if (!VLA) return; + // Check the VLA sizes for validity. llvm::SmallVector VLASizes; const VariableArrayType *VLALast = nullptr; - // Walk over the VLAs for every dimension until a non-VLA is found. - // Collect the sizes in VLASizes, put the most inner VLA to `VLALast`. - // In "vla[x][2][y][3]" this will be the array for index "y". - // There is a VariableArrayType for every dimension (here "x", "2", "y") - // until a non-vla is found. - while (VLA) { - const Expr *SizeE = VLA->getSizeExpr(); - State = checkVLASize(C, State, SizeE); - if (!State) - return; - VLASizes.push_back(SizeE); - VLALast = VLA; - VLA = Ctx.getAsVariableArrayType(VLA->getElementType()); - }; - assert(VLALast && - "Array should have at least one variably-modified dimension."); + + State = checkVLA(C, State, VLA, VLALast, VLASizes); + if (!State) + return; + + if (!VD) + return; // VLASizeChecker is responsible for defining the extent of the array being // declared. We do this by multiplying the array length by the element size, @@ -219,6 +253,33 @@ C.addTransition(State); } +void VLASizeChecker::checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE, + CheckerContext &C) const { + // Want to check for sizeof. + if (UETTE->getKind() != UETT_SizeOf) + return; + + // Ensure a type argument. + if (!UETTE->isArgumentType()) + return; + + const VariableArrayType *VLA = + C.getASTContext().getAsVariableArrayType(UETTE->getTypeOfArgument()); + // Ensure that the type is a VLA. + if (!VLA) + return; + + ProgramStateRef State = C.getState(); + + llvm::SmallVector VLASizes; + const VariableArrayType *VLALast = nullptr; + State = checkVLA(C, State, VLA, VLALast, VLASizes); + if (!State) + return; + + C.addTransition(State); +} + void ento::registerVLASizeChecker(CheckerManager &mgr) { mgr.registerChecker(); } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -573,6 +573,13 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + if (isa(*DS->decl_begin())) { + ExplodedNodeSet DstPre; + getCheckerManager().runCheckersForPreStmt(DstPre, Pred, DS, *this); + getCheckerManager().runCheckersForPostStmt(Dst, DstPre, DS, *this); + return; + } + // Assumption: The CFG has one DeclStmt per Decl. const VarDecl *VD = dyn_cast_or_null(*DS->decl_begin()); diff --git a/clang/test/Analysis/vla.c b/clang/test/Analysis/vla.c --- a/clang/test/Analysis/vla.c +++ b/clang/test/Analysis/vla.c @@ -89,6 +89,17 @@ check_negative_sized_VLA_11_sub(x); } +void check_VLA_typedef() { + int x = -1; + typedef int VLA[x]; // expected-warning{{Declared variable-length array (VLA) has negative size}} +} + +size_t check_VLA_sizeof() { + int x = -1; + size_t s = sizeof(int[x]); // expected-warning{{Declared variable-length array (VLA) has negative size}} + return s; +} + // Multi-dimensional arrays. void check_zero_sized_VLA_multi1(int x) {