1eaa7d00fSGabor Marton //==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==// 2eaa7d00fSGabor Marton // 3eaa7d00fSGabor Marton // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4eaa7d00fSGabor Marton // See https://llvm.org/LICENSE.txt for license information. 5eaa7d00fSGabor Marton // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6eaa7d00fSGabor Marton // 7eaa7d00fSGabor Marton //===----------------------------------------------------------------------===// 8eaa7d00fSGabor Marton // 9eaa7d00fSGabor Marton // This file defines a check for misuse of the default placement new operator. 10eaa7d00fSGabor Marton // 11eaa7d00fSGabor Marton //===----------------------------------------------------------------------===// 12eaa7d00fSGabor Marton 135e7beb0aSGabor Marton #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 145e7beb0aSGabor Marton #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 155e7beb0aSGabor Marton #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 169b3df78bSCharusso #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" 175e7beb0aSGabor Marton #include "llvm/Support/FormatVariadic.h" 185e7beb0aSGabor Marton 195e7beb0aSGabor Marton using namespace clang; 205e7beb0aSGabor Marton using namespace ento; 215e7beb0aSGabor Marton 22df186507SBenjamin Kramer namespace { 235e7beb0aSGabor Marton class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> { 245e7beb0aSGabor Marton public: 255e7beb0aSGabor Marton void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const; 265e7beb0aSGabor Marton 275e7beb0aSGabor Marton private: 287c376849SGabor Marton bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE, 297c376849SGabor Marton CheckerContext &C) const; 307c376849SGabor Marton 317c376849SGabor Marton bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE, 327c376849SGabor Marton CheckerContext &C) const; 337c376849SGabor Marton 345e7beb0aSGabor Marton // Returns the size of the target in a placement new expression. 355e7beb0aSGabor Marton // E.g. in "new (&s) long" it returns the size of `long`. 367c376849SGabor Marton SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C, 377c376849SGabor Marton bool &IsArray) const; 385e7beb0aSGabor Marton // Returns the size of the place in a placement new expression. 395e7beb0aSGabor Marton // E.g. in "new (&s) long" it returns the size of `s`. 407c376849SGabor Marton SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const; 417c376849SGabor Marton 427c376849SGabor Marton void emitBadAlignReport(const Expr *P, CheckerContext &C, 437c376849SGabor Marton unsigned AllocatedTAlign, 447c376849SGabor Marton unsigned StorageTAlign) const; 457c376849SGabor Marton unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const; 467c376849SGabor Marton 477c376849SGabor Marton void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C, 487c376849SGabor Marton const Expr *P, unsigned AllocatedTAlign) const; 497c376849SGabor Marton 507c376849SGabor Marton void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C, 517c376849SGabor Marton const Expr *P, unsigned AllocatedTAlign) const; 527c376849SGabor Marton 537c376849SGabor Marton bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C, 547c376849SGabor Marton const Expr *P, 557c376849SGabor Marton unsigned AllocatedTAlign) const; 567c376849SGabor Marton 577c376849SGabor Marton BugType SBT{this, "Insufficient storage for placement new", 587c376849SGabor Marton categories::MemoryError}; 597c376849SGabor Marton BugType ABT{this, "Bad align storage for placement new", 605e7beb0aSGabor Marton categories::MemoryError}; 615e7beb0aSGabor Marton }; 62df186507SBenjamin Kramer } // namespace 635e7beb0aSGabor Marton 647c376849SGabor Marton SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE, 655e7beb0aSGabor Marton CheckerContext &C) const { 667c376849SGabor Marton const Expr *Place = NE->getPlacementArg(0); 679b3df78bSCharusso return getDynamicExtentWithOffset(C.getState(), C.getSVal(Place)); 685e7beb0aSGabor Marton } 695e7beb0aSGabor Marton 705e7beb0aSGabor Marton SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE, 717c376849SGabor Marton CheckerContext &C, 727c376849SGabor Marton bool &IsArray) const { 737c376849SGabor Marton ProgramStateRef State = C.getState(); 745e7beb0aSGabor Marton SValBuilder &SvalBuilder = C.getSValBuilder(); 755e7beb0aSGabor Marton QualType ElementType = NE->getAllocatedType(); 765e7beb0aSGabor Marton ASTContext &AstContext = C.getASTContext(); 775e7beb0aSGabor Marton CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); 787c376849SGabor Marton IsArray = false; 795e7beb0aSGabor Marton if (NE->isArray()) { 807c376849SGabor Marton IsArray = true; 815e7beb0aSGabor Marton const Expr *SizeExpr = *NE->getArraySize(); 825e7beb0aSGabor Marton SVal ElementCount = C.getSVal(SizeExpr); 835e7beb0aSGabor Marton if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) { 845e7beb0aSGabor Marton // size in Bytes = ElementCountNL * TypeSize 855e7beb0aSGabor Marton return SvalBuilder.evalBinOp( 865e7beb0aSGabor Marton State, BO_Mul, *ElementCountNL, 875e7beb0aSGabor Marton SvalBuilder.makeArrayIndex(TypeSize.getQuantity()), 885e7beb0aSGabor Marton SvalBuilder.getArrayIndexType()); 895e7beb0aSGabor Marton } 905e7beb0aSGabor Marton } else { 915e7beb0aSGabor Marton // Create a concrete int whose size in bits and signedness is equal to 925e7beb0aSGabor Marton // ArrayIndexType. 935e7beb0aSGabor Marton llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType()) 945e7beb0aSGabor Marton .getQuantity() * 955e7beb0aSGabor Marton C.getASTContext().getCharWidth(), 965e7beb0aSGabor Marton TypeSize.getQuantity()); 975e7beb0aSGabor Marton return SvalBuilder.makeArrayIndex(I.getZExtValue()); 985e7beb0aSGabor Marton } 995e7beb0aSGabor Marton return UnknownVal(); 1005e7beb0aSGabor Marton } 1015e7beb0aSGabor Marton 1027c376849SGabor Marton bool PlacementNewChecker::checkPlaceCapacityIsSufficient( 1037c376849SGabor Marton const CXXNewExpr *NE, CheckerContext &C) const { 1047c376849SGabor Marton bool IsArrayTypeAllocated; 1057c376849SGabor Marton SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated); 1067c376849SGabor Marton SVal SizeOfPlace = getExtentSizeOfPlace(NE, C); 1077c376849SGabor Marton const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>(); 1087c376849SGabor Marton if (!SizeOfTargetCI) 1097c376849SGabor Marton return true; 1107c376849SGabor Marton const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>(); 1117c376849SGabor Marton if (!SizeOfPlaceCI) 1127c376849SGabor Marton return true; 1137c376849SGabor Marton 1147c376849SGabor Marton if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) || 1157c376849SGabor Marton (IsArrayTypeAllocated && 1167c376849SGabor Marton SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) { 1177c376849SGabor Marton if (ExplodedNode *N = C.generateErrorNode(C.getState())) { 1187c376849SGabor Marton std::string Msg; 1197c376849SGabor Marton // TODO: use clang constant 1207c376849SGabor Marton if (IsArrayTypeAllocated && 1217c376849SGabor Marton SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue()) 1227c376849SGabor Marton Msg = std::string(llvm::formatv( 1237c376849SGabor Marton "{0} bytes is possibly not enough for array allocation which " 1247c376849SGabor Marton "requires {1} bytes. Current overhead requires the size of {2} " 1257c376849SGabor Marton "bytes", 1267c376849SGabor Marton SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(), 127*d0d5101fSBalazs Benics *SizeOfPlaceCI->getValue().get() - SizeOfTargetCI->getValue())); 1287c376849SGabor Marton else if (IsArrayTypeAllocated && 1297c376849SGabor Marton SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue()) 1307c376849SGabor Marton Msg = std::string(llvm::formatv( 1317c376849SGabor Marton "Storage provided to placement new is only {0} bytes, " 1327c376849SGabor Marton "whereas the allocated array type requires more space for " 1337c376849SGabor Marton "internal needs", 134b75fe11fSRahul Joshi SizeOfPlaceCI->getValue())); 1357c376849SGabor Marton else 1367c376849SGabor Marton Msg = std::string(llvm::formatv( 1377c376849SGabor Marton "Storage provided to placement new is only {0} bytes, " 1387c376849SGabor Marton "whereas the allocated type requires {1} bytes", 1397c376849SGabor Marton SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue())); 1407c376849SGabor Marton 1417c376849SGabor Marton auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N); 1427c376849SGabor Marton bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R); 1437c376849SGabor Marton C.emitReport(std::move(R)); 1447c376849SGabor Marton 1457c376849SGabor Marton return false; 1467c376849SGabor Marton } 1477c376849SGabor Marton } 1487c376849SGabor Marton 1497c376849SGabor Marton return true; 1507c376849SGabor Marton } 1517c376849SGabor Marton 1527c376849SGabor Marton void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C, 1537c376849SGabor Marton unsigned AllocatedTAlign, 1547c376849SGabor Marton unsigned StorageTAlign) const { 1557c376849SGabor Marton ProgramStateRef State = C.getState(); 1567c376849SGabor Marton if (ExplodedNode *N = C.generateErrorNode(State)) { 1577c376849SGabor Marton std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but " 1587c376849SGabor Marton "allocated type is aligned to {1} bytes", 1597c376849SGabor Marton StorageTAlign, AllocatedTAlign)); 1607c376849SGabor Marton 1617c376849SGabor Marton auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N); 1627c376849SGabor Marton bugreporter::trackExpressionValue(N, P, *R); 1637c376849SGabor Marton C.emitReport(std::move(R)); 1647c376849SGabor Marton } 1657c376849SGabor Marton } 1667c376849SGabor Marton 1677c376849SGabor Marton unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C, 1687c376849SGabor Marton const ValueDecl *VD) const { 1697c376849SGabor Marton unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType()); 1707c376849SGabor Marton if (unsigned SpecifiedAlignment = VD->getMaxAlignment()) 1717c376849SGabor Marton StorageTAlign = SpecifiedAlignment; 1727c376849SGabor Marton 1737c376849SGabor Marton return StorageTAlign / C.getASTContext().getCharWidth(); 1747c376849SGabor Marton } 1757c376849SGabor Marton 1767c376849SGabor Marton void PlacementNewChecker::checkElementRegionAlign( 1777c376849SGabor Marton const ElementRegion *R, CheckerContext &C, const Expr *P, 1787c376849SGabor Marton unsigned AllocatedTAlign) const { 1797c376849SGabor Marton auto IsBaseRegionAlignedProperly = [this, R, &C, P, 1807c376849SGabor Marton AllocatedTAlign]() -> bool { 1817c376849SGabor Marton // Unwind nested ElementRegion`s to get the type. 1827c376849SGabor Marton const MemRegion *SuperRegion = R; 1837c376849SGabor Marton while (true) { 1847c376849SGabor Marton if (SuperRegion->getKind() == MemRegion::ElementRegionKind) { 1857c376849SGabor Marton SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion(); 1867c376849SGabor Marton continue; 1877c376849SGabor Marton } 1887c376849SGabor Marton 1897c376849SGabor Marton break; 1907c376849SGabor Marton } 1917c376849SGabor Marton 1927c376849SGabor Marton const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>(); 1937c376849SGabor Marton if (!TheElementDeclRegion) 1947c376849SGabor Marton return false; 1957c376849SGabor Marton 1967c376849SGabor Marton const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>(); 1977c376849SGabor Marton if (!BaseDeclRegion) 1987c376849SGabor Marton return false; 1997c376849SGabor Marton 2007c376849SGabor Marton unsigned BaseRegionAlign = 0; 2017c376849SGabor Marton // We must use alignment TheElementDeclRegion if it has its own alignment 2027c376849SGabor Marton // specifier 2037c376849SGabor Marton if (TheElementDeclRegion->getDecl()->getMaxAlignment()) 2047c376849SGabor Marton BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl()); 2057c376849SGabor Marton else 2067c376849SGabor Marton BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl()); 2077c376849SGabor Marton 2087c376849SGabor Marton if (AllocatedTAlign > BaseRegionAlign) { 2097c376849SGabor Marton emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign); 2107c376849SGabor Marton return false; 2117c376849SGabor Marton } 2127c376849SGabor Marton 2137c376849SGabor Marton return true; 2147c376849SGabor Marton }; 2157c376849SGabor Marton 2167c376849SGabor Marton auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void { 2177c376849SGabor Marton RegionOffset TheOffsetRegion = R->getAsOffset(); 2187c376849SGabor Marton if (TheOffsetRegion.hasSymbolicOffset()) 2197c376849SGabor Marton return; 2207c376849SGabor Marton 2217c376849SGabor Marton unsigned Offset = 2227c376849SGabor Marton TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth(); 2237c376849SGabor Marton unsigned AddressAlign = Offset % AllocatedTAlign; 2247c376849SGabor Marton if (AddressAlign != 0) { 2257c376849SGabor Marton emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign); 2267c376849SGabor Marton return; 2277c376849SGabor Marton } 2287c376849SGabor Marton }; 2297c376849SGabor Marton 2307c376849SGabor Marton if (IsBaseRegionAlignedProperly()) { 2317c376849SGabor Marton CheckElementRegionOffset(); 2327c376849SGabor Marton } 2337c376849SGabor Marton } 2347c376849SGabor Marton 2357c376849SGabor Marton void PlacementNewChecker::checkFieldRegionAlign( 2367c376849SGabor Marton const FieldRegion *R, CheckerContext &C, const Expr *P, 2377c376849SGabor Marton unsigned AllocatedTAlign) const { 2387c376849SGabor Marton const MemRegion *BaseRegion = R->getBaseRegion(); 2397c376849SGabor Marton if (!BaseRegion) 2407c376849SGabor Marton return; 2417c376849SGabor Marton 2427c376849SGabor Marton if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) { 2437c376849SGabor Marton if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) { 2447c376849SGabor Marton // We've checked type align but, unless FieldRegion 2457c376849SGabor Marton // offset is zero, we also need to check its own 2467c376849SGabor Marton // align. 2477c376849SGabor Marton RegionOffset Offset = R->getAsOffset(); 2487c376849SGabor Marton if (Offset.hasSymbolicOffset()) 2497c376849SGabor Marton return; 2507c376849SGabor Marton 2517c376849SGabor Marton int64_t OffsetValue = 2527c376849SGabor Marton Offset.getOffset() / C.getASTContext().getCharWidth(); 2537c376849SGabor Marton unsigned AddressAlign = OffsetValue % AllocatedTAlign; 2547c376849SGabor Marton if (AddressAlign != 0) 2557c376849SGabor Marton emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign); 2567c376849SGabor Marton } 2577c376849SGabor Marton } 2587c376849SGabor Marton } 2597c376849SGabor Marton 2607c376849SGabor Marton bool PlacementNewChecker::isVarRegionAlignedProperly( 2617c376849SGabor Marton const VarRegion *R, CheckerContext &C, const Expr *P, 2627c376849SGabor Marton unsigned AllocatedTAlign) const { 2637c376849SGabor Marton const VarDecl *TheVarDecl = R->getDecl(); 2647c376849SGabor Marton unsigned StorageTAlign = getStorageAlign(C, TheVarDecl); 2657c376849SGabor Marton if (AllocatedTAlign > StorageTAlign) { 2667c376849SGabor Marton emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign); 2677c376849SGabor Marton 2687c376849SGabor Marton return false; 2697c376849SGabor Marton } 2707c376849SGabor Marton 2717c376849SGabor Marton return true; 2727c376849SGabor Marton } 2737c376849SGabor Marton 2747c376849SGabor Marton bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE, 2757c376849SGabor Marton CheckerContext &C) const { 2767c376849SGabor Marton const Expr *Place = NE->getPlacementArg(0); 2777c376849SGabor Marton 2787c376849SGabor Marton QualType AllocatedT = NE->getAllocatedType(); 2797c376849SGabor Marton unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) / 2807c376849SGabor Marton C.getASTContext().getCharWidth(); 2817c376849SGabor Marton 2827c376849SGabor Marton SVal PlaceVal = C.getSVal(Place); 2837c376849SGabor Marton if (const MemRegion *MRegion = PlaceVal.getAsRegion()) { 2847c376849SGabor Marton if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>()) 2857c376849SGabor Marton checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign); 2867c376849SGabor Marton else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>()) 2877c376849SGabor Marton checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign); 2887c376849SGabor Marton else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>()) 2897c376849SGabor Marton isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign); 2907c376849SGabor Marton } 2917c376849SGabor Marton 2927c376849SGabor Marton return true; 2937c376849SGabor Marton } 2947c376849SGabor Marton 2955e7beb0aSGabor Marton void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE, 2965e7beb0aSGabor Marton CheckerContext &C) const { 2975e7beb0aSGabor Marton // Check only the default placement new. 2985e7beb0aSGabor Marton if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator()) 2995e7beb0aSGabor Marton return; 3007c376849SGabor Marton 3015e7beb0aSGabor Marton if (NE->getNumPlacementArgs() == 0) 3025e7beb0aSGabor Marton return; 3035e7beb0aSGabor Marton 3047c376849SGabor Marton if (!checkPlaceCapacityIsSufficient(NE, C)) 3055e7beb0aSGabor Marton return; 3065e7beb0aSGabor Marton 3077c376849SGabor Marton checkPlaceIsAlignedProperly(NE, C); 3085e7beb0aSGabor Marton } 3095e7beb0aSGabor Marton 3105e7beb0aSGabor Marton void ento::registerPlacementNewChecker(CheckerManager &mgr) { 3115e7beb0aSGabor Marton mgr.registerChecker<PlacementNewChecker>(); 3125e7beb0aSGabor Marton } 3135e7beb0aSGabor Marton 314bda3dd0dSKirstóf Umann bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) { 3155e7beb0aSGabor Marton return true; 3165e7beb0aSGabor Marton } 317