123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- //==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines a check for misuse of the default placement new operator.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
- #include "llvm/Support/FormatVariadic.h"
- using namespace clang;
- using namespace ento;
- namespace {
- class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
- public:
- void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
- private:
- bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,
- CheckerContext &C) const;
- bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
- CheckerContext &C) const;
- // Returns the size of the target in a placement new expression.
- // E.g. in "new (&s) long" it returns the size of `long`.
- SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,
- bool &IsArray) const;
- // Returns the size of the place in a placement new expression.
- // E.g. in "new (&s) long" it returns the size of `s`.
- SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;
- void emitBadAlignReport(const Expr *P, CheckerContext &C,
- unsigned AllocatedTAlign,
- unsigned StorageTAlign) const;
- unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;
- void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,
- const Expr *P, unsigned AllocatedTAlign) const;
- void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,
- const Expr *P, unsigned AllocatedTAlign) const;
- bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,
- const Expr *P,
- unsigned AllocatedTAlign) const;
- BugType SBT{this, "Insufficient storage for placement new",
- categories::MemoryError};
- BugType ABT{this, "Bad align storage for placement new",
- categories::MemoryError};
- };
- } // namespace
- SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,
- CheckerContext &C) const {
- const Expr *Place = NE->getPlacementArg(0);
- return getDynamicExtentWithOffset(C.getState(), C.getSVal(Place));
- }
- SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
- CheckerContext &C,
- bool &IsArray) const {
- ProgramStateRef State = C.getState();
- SValBuilder &SvalBuilder = C.getSValBuilder();
- QualType ElementType = NE->getAllocatedType();
- ASTContext &AstContext = C.getASTContext();
- CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
- IsArray = false;
- if (NE->isArray()) {
- IsArray = true;
- const Expr *SizeExpr = *NE->getArraySize();
- SVal ElementCount = C.getSVal(SizeExpr);
- if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
- // size in Bytes = ElementCountNL * TypeSize
- return SvalBuilder.evalBinOp(
- State, BO_Mul, *ElementCountNL,
- SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
- SvalBuilder.getArrayIndexType());
- }
- } else {
- // Create a concrete int whose size in bits and signedness is equal to
- // ArrayIndexType.
- llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
- .getQuantity() *
- C.getASTContext().getCharWidth(),
- TypeSize.getQuantity());
- return SvalBuilder.makeArrayIndex(I.getZExtValue());
- }
- return UnknownVal();
- }
- bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
- const CXXNewExpr *NE, CheckerContext &C) const {
- bool IsArrayTypeAllocated;
- SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);
- SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);
- const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
- if (!SizeOfTargetCI)
- return true;
- const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
- if (!SizeOfPlaceCI)
- return true;
- if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
- (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
- if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
- std::string Msg;
- // TODO: use clang constant
- if (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
- Msg = std::string(llvm::formatv(
- "{0} bytes is possibly not enough for array allocation which "
- "requires {1} bytes. Current overhead requires the size of {2} "
- "bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
- SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
- else if (IsArrayTypeAllocated &&
- SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
- Msg = std::string(llvm::formatv(
- "Storage provided to placement new is only {0} bytes, "
- "whereas the allocated array type requires more space for "
- "internal needs",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
- else
- Msg = std::string(llvm::formatv(
- "Storage provided to placement new is only {0} bytes, "
- "whereas the allocated type requires {1} bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
- auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
- bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
- C.emitReport(std::move(R));
- return false;
- }
- }
- return true;
- }
- void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,
- unsigned AllocatedTAlign,
- unsigned StorageTAlign) const {
- ProgramStateRef State = C.getState();
- if (ExplodedNode *N = C.generateErrorNode(State)) {
- std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
- "allocated type is aligned to {1} bytes",
- StorageTAlign, AllocatedTAlign));
- auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
- bugreporter::trackExpressionValue(N, P, *R);
- C.emitReport(std::move(R));
- }
- }
- unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
- const ValueDecl *VD) const {
- unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());
- if (unsigned SpecifiedAlignment = VD->getMaxAlignment())
- StorageTAlign = SpecifiedAlignment;
- return StorageTAlign / C.getASTContext().getCharWidth();
- }
- void PlacementNewChecker::checkElementRegionAlign(
- const ElementRegion *R, CheckerContext &C, const Expr *P,
- unsigned AllocatedTAlign) const {
- auto IsBaseRegionAlignedProperly = [this, R, &C, P,
- AllocatedTAlign]() -> bool {
- // Unwind nested ElementRegion`s to get the type.
- const MemRegion *SuperRegion = R;
- while (true) {
- if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
- SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
- continue;
- }
- break;
- }
- const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
- if (!TheElementDeclRegion)
- return false;
- const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
- if (!BaseDeclRegion)
- return false;
- unsigned BaseRegionAlign = 0;
- // We must use alignment TheElementDeclRegion if it has its own alignment
- // specifier
- if (TheElementDeclRegion->getDecl()->getMaxAlignment())
- BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
- else
- BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
- if (AllocatedTAlign > BaseRegionAlign) {
- emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
- return false;
- }
- return true;
- };
- auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
- RegionOffset TheOffsetRegion = R->getAsOffset();
- if (TheOffsetRegion.hasSymbolicOffset())
- return;
- unsigned Offset =
- TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
- unsigned AddressAlign = Offset % AllocatedTAlign;
- if (AddressAlign != 0) {
- emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
- return;
- }
- };
- if (IsBaseRegionAlignedProperly()) {
- CheckElementRegionOffset();
- }
- }
- void PlacementNewChecker::checkFieldRegionAlign(
- const FieldRegion *R, CheckerContext &C, const Expr *P,
- unsigned AllocatedTAlign) const {
- const MemRegion *BaseRegion = R->getBaseRegion();
- if (!BaseRegion)
- return;
- if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
- if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {
- // We've checked type align but, unless FieldRegion
- // offset is zero, we also need to check its own
- // align.
- RegionOffset Offset = R->getAsOffset();
- if (Offset.hasSymbolicOffset())
- return;
- int64_t OffsetValue =
- Offset.getOffset() / C.getASTContext().getCharWidth();
- unsigned AddressAlign = OffsetValue % AllocatedTAlign;
- if (AddressAlign != 0)
- emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
- }
- }
- }
- bool PlacementNewChecker::isVarRegionAlignedProperly(
- const VarRegion *R, CheckerContext &C, const Expr *P,
- unsigned AllocatedTAlign) const {
- const VarDecl *TheVarDecl = R->getDecl();
- unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);
- if (AllocatedTAlign > StorageTAlign) {
- emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);
- return false;
- }
- return true;
- }
- bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
- CheckerContext &C) const {
- const Expr *Place = NE->getPlacementArg(0);
- QualType AllocatedT = NE->getAllocatedType();
- unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
- C.getASTContext().getCharWidth();
- SVal PlaceVal = C.getSVal(Place);
- if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {
- if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())
- checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);
- else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())
- checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);
- else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())
- isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);
- }
- return true;
- }
- void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
- // Check only the default placement new.
- if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
- return;
- if (NE->getNumPlacementArgs() == 0)
- return;
- if (!checkPlaceCapacityIsSufficient(NE, C))
- return;
- checkPlaceIsAlignedProperly(NE, C);
- }
- void ento::registerPlacementNewChecker(CheckerManager &mgr) {
- mgr.registerChecker<PlacementNewChecker>();
- }
- bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
- return true;
- }
|