1 //=== CastSizeChecker.cpp ---------------------------------------*- 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 // CastSizeChecker checks when casting a malloc'ed symbolic region to type T, 10 // whether the size of the symbolic region is a multiple of the size of T. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/AST/CharUnits.h" 15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" 21 22 using namespace clang; 23 using namespace ento; 24 25 namespace { 26 class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > { 27 const BugType BT{this, "Cast region with wrong size."}; 28 29 public: 30 void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; 31 }; 32 } 33 34 /// Check if we are casting to a struct with a flexible array at the end. 35 /// \code 36 /// struct foo { 37 /// size_t len; 38 /// struct bar data[]; 39 /// }; 40 /// \endcode 41 /// or 42 /// \code 43 /// struct foo { 44 /// size_t len; 45 /// struct bar data[0]; 46 /// } 47 /// \endcode 48 /// In these cases it is also valid to allocate size of struct foo + a multiple 49 /// of struct bar. 50 static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize, 51 CharUnits TypeSize, QualType ToPointeeTy) { 52 const RecordType *RT = ToPointeeTy->getAs<RecordType>(); 53 if (!RT) 54 return false; 55 56 const RecordDecl *RD = RT->getDecl(); 57 RecordDecl::field_iterator Iter(RD->field_begin()); 58 RecordDecl::field_iterator End(RD->field_end()); 59 const FieldDecl *Last = nullptr; 60 for (; Iter != End; ++Iter) 61 Last = *Iter; 62 assert(Last && "empty structs should already be handled"); 63 64 const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual(); 65 if (!ElemType) 66 return false; 67 CharUnits FlexSize; 68 if (const ConstantArrayType *ArrayTy = 69 Ctx.getAsConstantArrayType(Last->getType())) { 70 FlexSize = Ctx.getTypeSizeInChars(ElemType); 71 if (ArrayTy->getSize() == 1 && TypeSize > FlexSize) 72 TypeSize -= FlexSize; 73 else if (!ArrayTy->isZeroSize()) 74 return false; 75 } else if (RD->hasFlexibleArrayMember()) { 76 FlexSize = Ctx.getTypeSizeInChars(ElemType); 77 } else { 78 return false; 79 } 80 81 if (FlexSize.isZero()) 82 return false; 83 84 CharUnits Left = RegionSize - TypeSize; 85 if (Left.isNegative()) 86 return false; 87 88 return Left % FlexSize == 0; 89 } 90 91 void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { 92 const Expr *E = CE->getSubExpr(); 93 ASTContext &Ctx = C.getASTContext(); 94 QualType ToTy = Ctx.getCanonicalType(CE->getType()); 95 const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); 96 97 if (!ToPTy) 98 return; 99 100 QualType ToPointeeTy = ToPTy->getPointeeType(); 101 102 // Only perform the check if 'ToPointeeTy' is a complete type. 103 if (ToPointeeTy->isIncompleteType()) 104 return; 105 106 ProgramStateRef state = C.getState(); 107 const MemRegion *R = C.getSVal(E).getAsRegion(); 108 if (!R) 109 return; 110 111 const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); 112 if (!SR) 113 return; 114 115 SValBuilder &svalBuilder = C.getSValBuilder(); 116 117 DefinedOrUnknownSVal Size = getDynamicExtent(state, SR, svalBuilder); 118 const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); 119 if (!SizeInt) 120 return; 121 122 CharUnits regionSize = CharUnits::fromQuantity(SizeInt->getZExtValue()); 123 CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); 124 125 // Ignore void, and a few other un-sizeable types. 126 if (typeSize.isZero()) 127 return; 128 129 if (regionSize % typeSize == 0) 130 return; 131 132 if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) 133 return; 134 135 if (ExplodedNode *errorNode = C.generateErrorNode()) { 136 constexpr llvm::StringLiteral Msg = 137 "Cast a region whose size is not a multiple of the destination type " 138 "size."; 139 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, errorNode); 140 R->addRange(CE->getSourceRange()); 141 C.emitReport(std::move(R)); 142 } 143 } 144 145 void ento::registerCastSizeChecker(CheckerManager &mgr) { 146 mgr.registerChecker<CastSizeChecker>(); 147 } 148 149 bool ento::shouldRegisterCastSizeChecker(const CheckerManager &mgr) { 150 // PR31226: C++ is more complicated than what this checker currently supports. 151 // There are derived-to-base casts, there are different rules for 0-size 152 // structures, no flexible arrays, etc. 153 // FIXME: Disabled on C++ for now. 154 const LangOptions &LO = mgr.getLangOpts(); 155 return !LO.CPlusPlus; 156 } 157