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