brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.9 KiB · a227ca0 Raw
297 lines · cpp
1//==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9//  This file defines a check for misuse of the default placement new operator.10//11//===----------------------------------------------------------------------===//12 13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"14#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"15#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"16#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"17#include "llvm/Support/FormatVariadic.h"18 19using namespace clang;20using namespace ento;21 22namespace {23class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {24public:25  void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;26 27private:28  bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,29                                      CheckerContext &C) const;30 31  bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,32                                   CheckerContext &C) const;33 34  // Returns the size of the target in a placement new expression.35  // E.g. in "new (&s) long" it returns the size of `long`.36  SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,37                                bool &IsArray) const;38  // Returns the size of the place in a placement new expression.39  // E.g. in "new (&s) long" it returns the size of `s`.40  SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;41 42  void emitBadAlignReport(const Expr *P, CheckerContext &C,43                          unsigned AllocatedTAlign,44                          unsigned StorageTAlign) const;45  unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;46 47  void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,48                               const Expr *P, unsigned AllocatedTAlign) const;49 50  void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,51                             const Expr *P, unsigned AllocatedTAlign) const;52 53  bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,54                                  const Expr *P,55                                  unsigned AllocatedTAlign) const;56 57  BugType SBT{this, "Insufficient storage for placement new",58              categories::MemoryError};59  BugType ABT{this, "Bad align storage for placement new",60              categories::MemoryError};61};62} // namespace63 64SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,65                                               CheckerContext &C) const {66  const Expr *Place = NE->getPlacementArg(0);67  return getDynamicExtentWithOffset(C.getState(), C.getSVal(Place));68}69 70SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,71                                                   CheckerContext &C,72                                                   bool &IsArray) const {73  ProgramStateRef State = C.getState();74  SValBuilder &SvalBuilder = C.getSValBuilder();75  QualType ElementType = NE->getAllocatedType();76  ASTContext &AstContext = C.getASTContext();77  CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);78  IsArray = false;79  if (NE->isArray()) {80    IsArray = true;81    const Expr *SizeExpr = *NE->getArraySize();82    SVal ElementCount = C.getSVal(SizeExpr);83    if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {84      // size in Bytes = ElementCountNL * TypeSize85      return SvalBuilder.evalBinOp(86          State, BO_Mul, *ElementCountNL,87          SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),88          SvalBuilder.getArrayIndexType());89    }90  } else {91    // Create a concrete int whose size in bits and signedness is equal to92    // ArrayIndexType.93    llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())94                          .getQuantity() *95                      C.getASTContext().getCharWidth(),96                  TypeSize.getQuantity());97    return SvalBuilder.makeArrayIndex(I.getZExtValue());98  }99  return UnknownVal();100}101 102bool PlacementNewChecker::checkPlaceCapacityIsSufficient(103    const CXXNewExpr *NE, CheckerContext &C) const {104  bool IsArrayTypeAllocated;105  SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);106  SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);107  const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();108  if (!SizeOfTargetCI)109    return true;110  const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();111  if (!SizeOfPlaceCI)112    return true;113 114  if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue())) {115    if (ExplodedNode *N = C.generateErrorNode(C.getState())) {116      std::string Msg =117          llvm::formatv("Storage provided to placement new is only {0} bytes, "118                        "whereas the allocated type requires {1} bytes",119                        SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());120 121      auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);122      bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);123      C.emitReport(std::move(R));124 125      return false;126    }127  }128 129  return true;130}131 132void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,133                                             unsigned AllocatedTAlign,134                                             unsigned StorageTAlign) const {135  ProgramStateRef State = C.getState();136  if (ExplodedNode *N = C.generateErrorNode(State)) {137    std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "138                                  "allocated type is aligned to {1} bytes",139                                  StorageTAlign, AllocatedTAlign));140 141    auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);142    bugreporter::trackExpressionValue(N, P, *R);143    C.emitReport(std::move(R));144  }145}146 147unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,148                                              const ValueDecl *VD) const {149  unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());150  if (unsigned SpecifiedAlignment = VD->getMaxAlignment())151    StorageTAlign = SpecifiedAlignment;152 153  return StorageTAlign / C.getASTContext().getCharWidth();154}155 156void PlacementNewChecker::checkElementRegionAlign(157    const ElementRegion *R, CheckerContext &C, const Expr *P,158    unsigned AllocatedTAlign) const {159  auto IsBaseRegionAlignedProperly = [this, R, &C, P,160                                      AllocatedTAlign]() -> bool {161    // Unwind nested ElementRegion`s to get the type.162    const MemRegion *SuperRegion = R;163    while (true) {164      if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {165        SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();166        continue;167      }168 169      break;170    }171 172    const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();173    if (!TheElementDeclRegion)174      return false;175 176    const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();177    if (!BaseDeclRegion)178      return false;179 180    unsigned BaseRegionAlign = 0;181    // We must use alignment TheElementDeclRegion if it has its own alignment182    // specifier183    if (TheElementDeclRegion->getDecl()->getMaxAlignment())184      BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());185    else186      BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());187 188    if (AllocatedTAlign > BaseRegionAlign) {189      emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);190      return false;191    }192 193    return true;194  };195 196  auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {197    RegionOffset TheOffsetRegion = R->getAsOffset();198    if (TheOffsetRegion.hasSymbolicOffset())199      return;200 201    unsigned Offset =202        TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();203    unsigned AddressAlign = Offset % AllocatedTAlign;204    if (AddressAlign != 0) {205      emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);206      return;207    }208  };209 210  if (IsBaseRegionAlignedProperly()) {211    CheckElementRegionOffset();212  }213}214 215void PlacementNewChecker::checkFieldRegionAlign(216    const FieldRegion *R, CheckerContext &C, const Expr *P,217    unsigned AllocatedTAlign) const {218  const MemRegion *BaseRegion = R->getBaseRegion();219  if (!BaseRegion)220    return;221 222  if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {223    if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {224      // We've checked type align but, unless FieldRegion225      // offset is zero, we also need to check its own226      // align.227      RegionOffset Offset = R->getAsOffset();228      if (Offset.hasSymbolicOffset())229        return;230 231      int64_t OffsetValue =232          Offset.getOffset() / C.getASTContext().getCharWidth();233      unsigned AddressAlign = OffsetValue % AllocatedTAlign;234      if (AddressAlign != 0)235        emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);236    }237  }238}239 240bool PlacementNewChecker::isVarRegionAlignedProperly(241    const VarRegion *R, CheckerContext &C, const Expr *P,242    unsigned AllocatedTAlign) const {243  const VarDecl *TheVarDecl = R->getDecl();244  unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);245  if (AllocatedTAlign > StorageTAlign) {246    emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);247 248    return false;249  }250 251  return true;252}253 254bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,255                                                      CheckerContext &C) const {256  const Expr *Place = NE->getPlacementArg(0);257 258  QualType AllocatedT = NE->getAllocatedType();259  unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /260                             C.getASTContext().getCharWidth();261 262  SVal PlaceVal = C.getSVal(Place);263  if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {264    if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())265      checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);266    else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())267      checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);268    else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())269      isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);270  }271 272  return true;273}274 275void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,276                                       CheckerContext &C) const {277  // Check only the default placement new.278  if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())279    return;280 281  if (NE->getNumPlacementArgs() == 0)282    return;283 284  if (!checkPlaceCapacityIsSufficient(NE, C))285    return;286 287  checkPlaceIsAlignedProperly(NE, C);288}289 290void ento::registerPlacementNewChecker(CheckerManager &mgr) {291  mgr.registerChecker<PlacementNewChecker>();292}293 294bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {295  return true;296}297