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