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-exception 6 // 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/DynamicSize.h" 17 #include "llvm/Support/FormatVariadic.h" 18 19 using namespace clang; 20 using namespace ento; 21 22 namespace { 23 class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> { 24 public: 25 void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const; 26 27 private: 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 } // namespace 63 64 SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE, 65 CheckerContext &C) const { 66 ProgramStateRef State = C.getState(); 67 const Expr *Place = NE->getPlacementArg(0); 68 69 const MemRegion *MRegion = C.getSVal(Place).getAsRegion(); 70 if (!MRegion) 71 return UnknownVal(); 72 RegionOffset Offset = MRegion->getAsOffset(); 73 if (Offset.hasSymbolicOffset()) 74 return UnknownVal(); 75 const MemRegion *BaseRegion = MRegion->getBaseRegion(); 76 if (!BaseRegion) 77 return UnknownVal(); 78 79 SValBuilder &SvalBuilder = C.getSValBuilder(); 80 NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( 81 Offset.getOffset() / C.getASTContext().getCharWidth()); 82 DefinedOrUnknownSVal ExtentInBytes = 83 getDynamicSize(State, BaseRegion, SvalBuilder); 84 85 return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, 86 ExtentInBytes, OffsetInBytes, 87 SvalBuilder.getArrayIndexType()); 88 } 89 90 SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE, 91 CheckerContext &C, 92 bool &IsArray) const { 93 ProgramStateRef State = C.getState(); 94 SValBuilder &SvalBuilder = C.getSValBuilder(); 95 QualType ElementType = NE->getAllocatedType(); 96 ASTContext &AstContext = C.getASTContext(); 97 CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); 98 IsArray = false; 99 if (NE->isArray()) { 100 IsArray = true; 101 const Expr *SizeExpr = *NE->getArraySize(); 102 SVal ElementCount = C.getSVal(SizeExpr); 103 if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) { 104 // size in Bytes = ElementCountNL * TypeSize 105 return SvalBuilder.evalBinOp( 106 State, BO_Mul, *ElementCountNL, 107 SvalBuilder.makeArrayIndex(TypeSize.getQuantity()), 108 SvalBuilder.getArrayIndexType()); 109 } 110 } else { 111 // Create a concrete int whose size in bits and signedness is equal to 112 // ArrayIndexType. 113 llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType()) 114 .getQuantity() * 115 C.getASTContext().getCharWidth(), 116 TypeSize.getQuantity()); 117 return SvalBuilder.makeArrayIndex(I.getZExtValue()); 118 } 119 return UnknownVal(); 120 } 121 122 bool PlacementNewChecker::checkPlaceCapacityIsSufficient( 123 const CXXNewExpr *NE, CheckerContext &C) const { 124 bool IsArrayTypeAllocated; 125 SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated); 126 SVal SizeOfPlace = getExtentSizeOfPlace(NE, C); 127 const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>(); 128 if (!SizeOfTargetCI) 129 return true; 130 const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>(); 131 if (!SizeOfPlaceCI) 132 return true; 133 134 if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) || 135 (IsArrayTypeAllocated && 136 SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) { 137 if (ExplodedNode *N = C.generateErrorNode(C.getState())) { 138 std::string Msg; 139 // TODO: use clang constant 140 if (IsArrayTypeAllocated && 141 SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue()) 142 Msg = std::string(llvm::formatv( 143 "{0} bytes is possibly not enough for array allocation which " 144 "requires {1} bytes. Current overhead requires the size of {2} " 145 "bytes", 146 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(), 147 SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue())); 148 else if (IsArrayTypeAllocated && 149 SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue()) 150 Msg = std::string(llvm::formatv( 151 "Storage provided to placement new is only {0} bytes, " 152 "whereas the allocated array type requires more space for " 153 "internal needs", 154 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue())); 155 else 156 Msg = std::string(llvm::formatv( 157 "Storage provided to placement new is only {0} bytes, " 158 "whereas the allocated type requires {1} bytes", 159 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue())); 160 161 auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N); 162 bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R); 163 C.emitReport(std::move(R)); 164 165 return false; 166 } 167 } 168 169 return true; 170 } 171 172 void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C, 173 unsigned AllocatedTAlign, 174 unsigned StorageTAlign) const { 175 ProgramStateRef State = C.getState(); 176 if (ExplodedNode *N = C.generateErrorNode(State)) { 177 std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but " 178 "allocated type is aligned to {1} bytes", 179 StorageTAlign, AllocatedTAlign)); 180 181 auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N); 182 bugreporter::trackExpressionValue(N, P, *R); 183 C.emitReport(std::move(R)); 184 } 185 } 186 187 unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C, 188 const ValueDecl *VD) const { 189 unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType()); 190 if (unsigned SpecifiedAlignment = VD->getMaxAlignment()) 191 StorageTAlign = SpecifiedAlignment; 192 193 return StorageTAlign / C.getASTContext().getCharWidth(); 194 } 195 196 void PlacementNewChecker::checkElementRegionAlign( 197 const ElementRegion *R, CheckerContext &C, const Expr *P, 198 unsigned AllocatedTAlign) const { 199 auto IsBaseRegionAlignedProperly = [this, R, &C, P, 200 AllocatedTAlign]() -> bool { 201 // Unwind nested ElementRegion`s to get the type. 202 const MemRegion *SuperRegion = R; 203 while (true) { 204 if (SuperRegion->getKind() == MemRegion::ElementRegionKind) { 205 SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion(); 206 continue; 207 } 208 209 break; 210 } 211 212 const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>(); 213 if (!TheElementDeclRegion) 214 return false; 215 216 const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>(); 217 if (!BaseDeclRegion) 218 return false; 219 220 unsigned BaseRegionAlign = 0; 221 // We must use alignment TheElementDeclRegion if it has its own alignment 222 // specifier 223 if (TheElementDeclRegion->getDecl()->getMaxAlignment()) 224 BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl()); 225 else 226 BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl()); 227 228 if (AllocatedTAlign > BaseRegionAlign) { 229 emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign); 230 return false; 231 } 232 233 return true; 234 }; 235 236 auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void { 237 RegionOffset TheOffsetRegion = R->getAsOffset(); 238 if (TheOffsetRegion.hasSymbolicOffset()) 239 return; 240 241 unsigned Offset = 242 TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth(); 243 unsigned AddressAlign = Offset % AllocatedTAlign; 244 if (AddressAlign != 0) { 245 emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign); 246 return; 247 } 248 }; 249 250 if (IsBaseRegionAlignedProperly()) { 251 CheckElementRegionOffset(); 252 } 253 } 254 255 void PlacementNewChecker::checkFieldRegionAlign( 256 const FieldRegion *R, CheckerContext &C, const Expr *P, 257 unsigned AllocatedTAlign) const { 258 const MemRegion *BaseRegion = R->getBaseRegion(); 259 if (!BaseRegion) 260 return; 261 262 if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) { 263 if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) { 264 // We've checked type align but, unless FieldRegion 265 // offset is zero, we also need to check its own 266 // align. 267 RegionOffset Offset = R->getAsOffset(); 268 if (Offset.hasSymbolicOffset()) 269 return; 270 271 int64_t OffsetValue = 272 Offset.getOffset() / C.getASTContext().getCharWidth(); 273 unsigned AddressAlign = OffsetValue % AllocatedTAlign; 274 if (AddressAlign != 0) 275 emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign); 276 } 277 } 278 } 279 280 bool PlacementNewChecker::isVarRegionAlignedProperly( 281 const VarRegion *R, CheckerContext &C, const Expr *P, 282 unsigned AllocatedTAlign) const { 283 const VarDecl *TheVarDecl = R->getDecl(); 284 unsigned StorageTAlign = getStorageAlign(C, TheVarDecl); 285 if (AllocatedTAlign > StorageTAlign) { 286 emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign); 287 288 return false; 289 } 290 291 return true; 292 } 293 294 bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE, 295 CheckerContext &C) const { 296 const Expr *Place = NE->getPlacementArg(0); 297 298 QualType AllocatedT = NE->getAllocatedType(); 299 unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) / 300 C.getASTContext().getCharWidth(); 301 302 SVal PlaceVal = C.getSVal(Place); 303 if (const MemRegion *MRegion = PlaceVal.getAsRegion()) { 304 if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>()) 305 checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign); 306 else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>()) 307 checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign); 308 else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>()) 309 isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign); 310 } 311 312 return true; 313 } 314 315 void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE, 316 CheckerContext &C) const { 317 // Check only the default placement new. 318 if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator()) 319 return; 320 321 if (NE->getNumPlacementArgs() == 0) 322 return; 323 324 if (!checkPlaceCapacityIsSufficient(NE, C)) 325 return; 326 327 checkPlaceIsAlignedProperly(NE, C); 328 } 329 330 void ento::registerPlacementNewChecker(CheckerManager &mgr) { 331 mgr.registerChecker<PlacementNewChecker>(); 332 } 333 334 bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) { 335 return true; 336 } 337