xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp (revision d0d5101f9959013e42f6f07d79d0fe638aaa0aa3)
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