Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
35269 views
//==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines a check for misuse of the default placement new operator.9//10//===----------------------------------------------------------------------===//1112#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"13#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"14#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"15#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"16#include "llvm/Support/FormatVariadic.h"1718using namespace clang;19using namespace ento;2021namespace {22class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {23public:24void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;2526private:27bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,28CheckerContext &C) const;2930bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,31CheckerContext &C) const;3233// Returns the size of the target in a placement new expression.34// E.g. in "new (&s) long" it returns the size of `long`.35SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,36bool &IsArray) const;37// Returns the size of the place in a placement new expression.38// E.g. in "new (&s) long" it returns the size of `s`.39SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;4041void emitBadAlignReport(const Expr *P, CheckerContext &C,42unsigned AllocatedTAlign,43unsigned StorageTAlign) const;44unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;4546void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,47const Expr *P, unsigned AllocatedTAlign) const;4849void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,50const Expr *P, unsigned AllocatedTAlign) const;5152bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,53const Expr *P,54unsigned AllocatedTAlign) const;5556BugType SBT{this, "Insufficient storage for placement new",57categories::MemoryError};58BugType ABT{this, "Bad align storage for placement new",59categories::MemoryError};60};61} // namespace6263SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,64CheckerContext &C) const {65const Expr *Place = NE->getPlacementArg(0);66return getDynamicExtentWithOffset(C.getState(), C.getSVal(Place));67}6869SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,70CheckerContext &C,71bool &IsArray) const {72ProgramStateRef State = C.getState();73SValBuilder &SvalBuilder = C.getSValBuilder();74QualType ElementType = NE->getAllocatedType();75ASTContext &AstContext = C.getASTContext();76CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);77IsArray = false;78if (NE->isArray()) {79IsArray = true;80const Expr *SizeExpr = *NE->getArraySize();81SVal ElementCount = C.getSVal(SizeExpr);82if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {83// size in Bytes = ElementCountNL * TypeSize84return SvalBuilder.evalBinOp(85State, BO_Mul, *ElementCountNL,86SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),87SvalBuilder.getArrayIndexType());88}89} else {90// Create a concrete int whose size in bits and signedness is equal to91// ArrayIndexType.92llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())93.getQuantity() *94C.getASTContext().getCharWidth(),95TypeSize.getQuantity());96return SvalBuilder.makeArrayIndex(I.getZExtValue());97}98return UnknownVal();99}100101bool PlacementNewChecker::checkPlaceCapacityIsSufficient(102const CXXNewExpr *NE, CheckerContext &C) const {103bool IsArrayTypeAllocated;104SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);105SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);106const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();107if (!SizeOfTargetCI)108return true;109const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();110if (!SizeOfPlaceCI)111return true;112113if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||114(IsArrayTypeAllocated &&115SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {116if (ExplodedNode *N = C.generateErrorNode(C.getState())) {117std::string Msg;118// TODO: use clang constant119if (IsArrayTypeAllocated &&120SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())121Msg = std::string(llvm::formatv(122"{0} bytes is possibly not enough for array allocation which "123"requires {1} bytes. Current overhead requires the size of {2} "124"bytes",125SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),126SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));127else if (IsArrayTypeAllocated &&128SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())129Msg = std::string(llvm::formatv(130"Storage provided to placement new is only {0} bytes, "131"whereas the allocated array type requires more space for "132"internal needs",133SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));134else135Msg = std::string(llvm::formatv(136"Storage provided to placement new is only {0} bytes, "137"whereas the allocated type requires {1} bytes",138SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));139140auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);141bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);142C.emitReport(std::move(R));143144return false;145}146}147148return true;149}150151void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,152unsigned AllocatedTAlign,153unsigned StorageTAlign) const {154ProgramStateRef State = C.getState();155if (ExplodedNode *N = C.generateErrorNode(State)) {156std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "157"allocated type is aligned to {1} bytes",158StorageTAlign, AllocatedTAlign));159160auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);161bugreporter::trackExpressionValue(N, P, *R);162C.emitReport(std::move(R));163}164}165166unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,167const ValueDecl *VD) const {168unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());169if (unsigned SpecifiedAlignment = VD->getMaxAlignment())170StorageTAlign = SpecifiedAlignment;171172return StorageTAlign / C.getASTContext().getCharWidth();173}174175void PlacementNewChecker::checkElementRegionAlign(176const ElementRegion *R, CheckerContext &C, const Expr *P,177unsigned AllocatedTAlign) const {178auto IsBaseRegionAlignedProperly = [this, R, &C, P,179AllocatedTAlign]() -> bool {180// Unwind nested ElementRegion`s to get the type.181const MemRegion *SuperRegion = R;182while (true) {183if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {184SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();185continue;186}187188break;189}190191const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();192if (!TheElementDeclRegion)193return false;194195const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();196if (!BaseDeclRegion)197return false;198199unsigned BaseRegionAlign = 0;200// We must use alignment TheElementDeclRegion if it has its own alignment201// specifier202if (TheElementDeclRegion->getDecl()->getMaxAlignment())203BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());204else205BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());206207if (AllocatedTAlign > BaseRegionAlign) {208emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);209return false;210}211212return true;213};214215auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {216RegionOffset TheOffsetRegion = R->getAsOffset();217if (TheOffsetRegion.hasSymbolicOffset())218return;219220unsigned Offset =221TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();222unsigned AddressAlign = Offset % AllocatedTAlign;223if (AddressAlign != 0) {224emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);225return;226}227};228229if (IsBaseRegionAlignedProperly()) {230CheckElementRegionOffset();231}232}233234void PlacementNewChecker::checkFieldRegionAlign(235const FieldRegion *R, CheckerContext &C, const Expr *P,236unsigned AllocatedTAlign) const {237const MemRegion *BaseRegion = R->getBaseRegion();238if (!BaseRegion)239return;240241if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {242if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {243// We've checked type align but, unless FieldRegion244// offset is zero, we also need to check its own245// align.246RegionOffset Offset = R->getAsOffset();247if (Offset.hasSymbolicOffset())248return;249250int64_t OffsetValue =251Offset.getOffset() / C.getASTContext().getCharWidth();252unsigned AddressAlign = OffsetValue % AllocatedTAlign;253if (AddressAlign != 0)254emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);255}256}257}258259bool PlacementNewChecker::isVarRegionAlignedProperly(260const VarRegion *R, CheckerContext &C, const Expr *P,261unsigned AllocatedTAlign) const {262const VarDecl *TheVarDecl = R->getDecl();263unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);264if (AllocatedTAlign > StorageTAlign) {265emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);266267return false;268}269270return true;271}272273bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,274CheckerContext &C) const {275const Expr *Place = NE->getPlacementArg(0);276277QualType AllocatedT = NE->getAllocatedType();278unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /279C.getASTContext().getCharWidth();280281SVal PlaceVal = C.getSVal(Place);282if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {283if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())284checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);285else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())286checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);287else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())288isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);289}290291return true;292}293294void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,295CheckerContext &C) const {296// Check only the default placement new.297if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())298return;299300if (NE->getNumPlacementArgs() == 0)301return;302303if (!checkPlaceCapacityIsSufficient(NE, C))304return;305306checkPlaceIsAlignedProperly(NE, C);307}308309void ento::registerPlacementNewChecker(CheckerManager &mgr) {310mgr.registerChecker<PlacementNewChecker>();311}312313bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {314return true;315}316317318