1*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 2*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 3*480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 4*480093f4SDimitry Andric #include "llvm/Support/FormatVariadic.h" 5*480093f4SDimitry Andric 6*480093f4SDimitry Andric using namespace clang; 7*480093f4SDimitry Andric using namespace ento; 8*480093f4SDimitry Andric 9*480093f4SDimitry Andric namespace { 10*480093f4SDimitry Andric class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> { 11*480093f4SDimitry Andric public: 12*480093f4SDimitry Andric void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const; 13*480093f4SDimitry Andric 14*480093f4SDimitry Andric private: 15*480093f4SDimitry Andric // Returns the size of the target in a placement new expression. 16*480093f4SDimitry Andric // E.g. in "new (&s) long" it returns the size of `long`. 17*480093f4SDimitry Andric SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State, 18*480093f4SDimitry Andric CheckerContext &C) const; 19*480093f4SDimitry Andric // Returns the size of the place in a placement new expression. 20*480093f4SDimitry Andric // E.g. in "new (&s) long" it returns the size of `s`. 21*480093f4SDimitry Andric SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State, 22*480093f4SDimitry Andric CheckerContext &C) const; 23*480093f4SDimitry Andric BugType BT{this, "Insufficient storage for placement new", 24*480093f4SDimitry Andric categories::MemoryError}; 25*480093f4SDimitry Andric }; 26*480093f4SDimitry Andric } // namespace 27*480093f4SDimitry Andric 28*480093f4SDimitry Andric SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place, 29*480093f4SDimitry Andric ProgramStateRef State, 30*480093f4SDimitry Andric CheckerContext &C) const { 31*480093f4SDimitry Andric const MemRegion *MRegion = C.getSVal(Place).getAsRegion(); 32*480093f4SDimitry Andric if (!MRegion) 33*480093f4SDimitry Andric return UnknownVal(); 34*480093f4SDimitry Andric RegionOffset Offset = MRegion->getAsOffset(); 35*480093f4SDimitry Andric if (Offset.hasSymbolicOffset()) 36*480093f4SDimitry Andric return UnknownVal(); 37*480093f4SDimitry Andric const MemRegion *BaseRegion = MRegion->getBaseRegion(); 38*480093f4SDimitry Andric if (!BaseRegion) 39*480093f4SDimitry Andric return UnknownVal(); 40*480093f4SDimitry Andric 41*480093f4SDimitry Andric SValBuilder &SvalBuilder = C.getSValBuilder(); 42*480093f4SDimitry Andric NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( 43*480093f4SDimitry Andric Offset.getOffset() / C.getASTContext().getCharWidth()); 44*480093f4SDimitry Andric DefinedOrUnknownSVal ExtentInBytes = 45*480093f4SDimitry Andric BaseRegion->castAs<SubRegion>()->getExtent(SvalBuilder); 46*480093f4SDimitry Andric 47*480093f4SDimitry Andric return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, 48*480093f4SDimitry Andric ExtentInBytes, OffsetInBytes, 49*480093f4SDimitry Andric SvalBuilder.getArrayIndexType()); 50*480093f4SDimitry Andric } 51*480093f4SDimitry Andric 52*480093f4SDimitry Andric SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE, 53*480093f4SDimitry Andric ProgramStateRef State, 54*480093f4SDimitry Andric CheckerContext &C) const { 55*480093f4SDimitry Andric SValBuilder &SvalBuilder = C.getSValBuilder(); 56*480093f4SDimitry Andric QualType ElementType = NE->getAllocatedType(); 57*480093f4SDimitry Andric ASTContext &AstContext = C.getASTContext(); 58*480093f4SDimitry Andric CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); 59*480093f4SDimitry Andric if (NE->isArray()) { 60*480093f4SDimitry Andric const Expr *SizeExpr = *NE->getArraySize(); 61*480093f4SDimitry Andric SVal ElementCount = C.getSVal(SizeExpr); 62*480093f4SDimitry Andric if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) { 63*480093f4SDimitry Andric // size in Bytes = ElementCountNL * TypeSize 64*480093f4SDimitry Andric return SvalBuilder.evalBinOp( 65*480093f4SDimitry Andric State, BO_Mul, *ElementCountNL, 66*480093f4SDimitry Andric SvalBuilder.makeArrayIndex(TypeSize.getQuantity()), 67*480093f4SDimitry Andric SvalBuilder.getArrayIndexType()); 68*480093f4SDimitry Andric } 69*480093f4SDimitry Andric } else { 70*480093f4SDimitry Andric // Create a concrete int whose size in bits and signedness is equal to 71*480093f4SDimitry Andric // ArrayIndexType. 72*480093f4SDimitry Andric llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType()) 73*480093f4SDimitry Andric .getQuantity() * 74*480093f4SDimitry Andric C.getASTContext().getCharWidth(), 75*480093f4SDimitry Andric TypeSize.getQuantity()); 76*480093f4SDimitry Andric return SvalBuilder.makeArrayIndex(I.getZExtValue()); 77*480093f4SDimitry Andric } 78*480093f4SDimitry Andric return UnknownVal(); 79*480093f4SDimitry Andric } 80*480093f4SDimitry Andric 81*480093f4SDimitry Andric void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE, 82*480093f4SDimitry Andric CheckerContext &C) const { 83*480093f4SDimitry Andric // Check only the default placement new. 84*480093f4SDimitry Andric if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator()) 85*480093f4SDimitry Andric return; 86*480093f4SDimitry Andric if (NE->getNumPlacementArgs() == 0) 87*480093f4SDimitry Andric return; 88*480093f4SDimitry Andric 89*480093f4SDimitry Andric ProgramStateRef State = C.getState(); 90*480093f4SDimitry Andric SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C); 91*480093f4SDimitry Andric const Expr *Place = NE->getPlacementArg(0); 92*480093f4SDimitry Andric SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C); 93*480093f4SDimitry Andric const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>(); 94*480093f4SDimitry Andric if (!SizeOfTargetCI) 95*480093f4SDimitry Andric return; 96*480093f4SDimitry Andric const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>(); 97*480093f4SDimitry Andric if (!SizeOfPlaceCI) 98*480093f4SDimitry Andric return; 99*480093f4SDimitry Andric 100*480093f4SDimitry Andric if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) { 101*480093f4SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(State)) { 102*480093f4SDimitry Andric std::string Msg = 103*480093f4SDimitry Andric llvm::formatv("Storage provided to placement new is only {0} bytes, " 104*480093f4SDimitry Andric "whereas the allocated type requires {1} bytes", 105*480093f4SDimitry Andric SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()); 106*480093f4SDimitry Andric 107*480093f4SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); 108*480093f4SDimitry Andric bugreporter::trackExpressionValue(N, Place, *R); 109*480093f4SDimitry Andric C.emitReport(std::move(R)); 110*480093f4SDimitry Andric return; 111*480093f4SDimitry Andric } 112*480093f4SDimitry Andric } 113*480093f4SDimitry Andric } 114*480093f4SDimitry Andric 115*480093f4SDimitry Andric void ento::registerPlacementNewChecker(CheckerManager &mgr) { 116*480093f4SDimitry Andric mgr.registerChecker<PlacementNewChecker>(); 117*480093f4SDimitry Andric } 118*480093f4SDimitry Andric 119*480093f4SDimitry Andric bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) { 120*480093f4SDimitry Andric return true; 121*480093f4SDimitry Andric } 122