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 // Returns the size of the target in a placement new expression. 29 // E.g. in "new (&s) long" it returns the size of `long`. 30 SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State, 31 CheckerContext &C) const; 32 // Returns the size of the place in a placement new expression. 33 // E.g. in "new (&s) long" it returns the size of `s`. 34 SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State, 35 CheckerContext &C) const; 36 BugType BT{this, "Insufficient storage for placement new", 37 categories::MemoryError}; 38 }; 39 } // namespace 40 41 SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place, 42 ProgramStateRef State, 43 CheckerContext &C) const { 44 const MemRegion *MRegion = C.getSVal(Place).getAsRegion(); 45 if (!MRegion) 46 return UnknownVal(); 47 RegionOffset Offset = MRegion->getAsOffset(); 48 if (Offset.hasSymbolicOffset()) 49 return UnknownVal(); 50 const MemRegion *BaseRegion = MRegion->getBaseRegion(); 51 if (!BaseRegion) 52 return UnknownVal(); 53 54 SValBuilder &SvalBuilder = C.getSValBuilder(); 55 NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( 56 Offset.getOffset() / C.getASTContext().getCharWidth()); 57 DefinedOrUnknownSVal ExtentInBytes = 58 getDynamicSize(State, BaseRegion, SvalBuilder); 59 60 return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, 61 ExtentInBytes, OffsetInBytes, 62 SvalBuilder.getArrayIndexType()); 63 } 64 65 SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE, 66 ProgramStateRef State, 67 CheckerContext &C) const { 68 SValBuilder &SvalBuilder = C.getSValBuilder(); 69 QualType ElementType = NE->getAllocatedType(); 70 ASTContext &AstContext = C.getASTContext(); 71 CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); 72 if (NE->isArray()) { 73 const Expr *SizeExpr = *NE->getArraySize(); 74 SVal ElementCount = C.getSVal(SizeExpr); 75 if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) { 76 // size in Bytes = ElementCountNL * TypeSize 77 return SvalBuilder.evalBinOp( 78 State, BO_Mul, *ElementCountNL, 79 SvalBuilder.makeArrayIndex(TypeSize.getQuantity()), 80 SvalBuilder.getArrayIndexType()); 81 } 82 } else { 83 // Create a concrete int whose size in bits and signedness is equal to 84 // ArrayIndexType. 85 llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType()) 86 .getQuantity() * 87 C.getASTContext().getCharWidth(), 88 TypeSize.getQuantity()); 89 return SvalBuilder.makeArrayIndex(I.getZExtValue()); 90 } 91 return UnknownVal(); 92 } 93 94 void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE, 95 CheckerContext &C) const { 96 // Check only the default placement new. 97 if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator()) 98 return; 99 if (NE->getNumPlacementArgs() == 0) 100 return; 101 102 ProgramStateRef State = C.getState(); 103 SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C); 104 const Expr *Place = NE->getPlacementArg(0); 105 SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C); 106 const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>(); 107 if (!SizeOfTargetCI) 108 return; 109 const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>(); 110 if (!SizeOfPlaceCI) 111 return; 112 113 if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) { 114 if (ExplodedNode *N = C.generateErrorNode(State)) { 115 std::string Msg = std::string( 116 llvm::formatv("Storage provided to placement new is only {0} bytes, " 117 "whereas the allocated type requires {1} bytes", 118 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue())); 119 120 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); 121 bugreporter::trackExpressionValue(N, Place, *R); 122 C.emitReport(std::move(R)); 123 return; 124 } 125 } 126 } 127 128 void ento::registerPlacementNewChecker(CheckerManager &mgr) { 129 mgr.registerChecker<PlacementNewChecker>(); 130 } 131 132 bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) { 133 return true; 134 } 135