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 CharUnits FlexSize; 66 if (const ConstantArrayType *ArrayTy = 67 Ctx.getAsConstantArrayType(Last->getType())) { 68 FlexSize = Ctx.getTypeSizeInChars(ElemType); 69 if (ArrayTy->getSize() == 1 && TypeSize > FlexSize) 70 TypeSize -= FlexSize; 71 else if (ArrayTy->getSize() != 0) 72 return false; 73 } else if (RD->hasFlexibleArrayMember()) { 74 FlexSize = Ctx.getTypeSizeInChars(ElemType); 75 } else { 76 return false; 77 } 78 79 if (FlexSize.isZero()) 80 return false; 81 82 CharUnits Left = RegionSize - TypeSize; 83 if (Left.isNegative()) 84 return false; 85 86 return Left % FlexSize == 0; 87 } 88 89 void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { 90 const Expr *E = CE->getSubExpr(); 91 ASTContext &Ctx = C.getASTContext(); 92 QualType ToTy = Ctx.getCanonicalType(CE->getType()); 93 const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); 94 95 if (!ToPTy) 96 return; 97 98 QualType ToPointeeTy = ToPTy->getPointeeType(); 99 100 // Only perform the check if 'ToPointeeTy' is a complete type. 101 if (ToPointeeTy->isIncompleteType()) 102 return; 103 104 ProgramStateRef state = C.getState(); 105 const MemRegion *R = C.getSVal(E).getAsRegion(); 106 if (!R) 107 return; 108 109 const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); 110 if (!SR) 111 return; 112 113 SValBuilder &svalBuilder = C.getSValBuilder(); 114 115 DefinedOrUnknownSVal Size = getDynamicExtent(state, SR, svalBuilder); 116 const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); 117 if (!SizeInt) 118 return; 119 120 CharUnits regionSize = CharUnits::fromQuantity(SizeInt->getZExtValue()); 121 CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); 122 123 // Ignore void, and a few other un-sizeable types. 124 if (typeSize.isZero()) 125 return; 126 127 if (regionSize % typeSize == 0) 128 return; 129 130 if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) 131 return; 132 133 if (ExplodedNode *errorNode = C.generateErrorNode()) { 134 constexpr llvm::StringLiteral Msg = 135 "Cast a region whose size is not a multiple of the destination type " 136 "size."; 137 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, errorNode); 138 R->addRange(CE->getSourceRange()); 139 C.emitReport(std::move(R)); 140 } 141 } 142 143 void ento::registerCastSizeChecker(CheckerManager &mgr) { 144 mgr.registerChecker<CastSizeChecker>(); 145 } 146 147 bool ento::shouldRegisterCastSizeChecker(const CheckerManager &mgr) { 148 // PR31226: C++ is more complicated than what this checker currently supports. 149 // There are derived-to-base casts, there are different rules for 0-size 150 // structures, no flexible arrays, etc. 151 // FIXME: Disabled on C++ for now. 152 const LangOptions &LO = mgr.getLangOpts(); 153 return !LO.CPlusPlus; 154 } 155