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