| #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| using namespace clang; |
| using namespace ento; |
| |
| namespace { |
| class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> { |
| public: |
| void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const; |
| |
| private: |
| // Returns the size of the target in a placement new expression. |
| // E.g. in "new (&s) long" it returns the size of `long`. |
| SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State, |
| CheckerContext &C) const; |
| // Returns the size of the place in a placement new expression. |
| // E.g. in "new (&s) long" it returns the size of `s`. |
| SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State, |
| CheckerContext &C) const; |
| BugType BT{this, "Insufficient storage for placement new", |
| categories::MemoryError}; |
| }; |
| } // namespace |
| |
| SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place, |
| ProgramStateRef State, |
| CheckerContext &C) const { |
| const MemRegion *MRegion = C.getSVal(Place).getAsRegion(); |
| if (!MRegion) |
| return UnknownVal(); |
| RegionOffset Offset = MRegion->getAsOffset(); |
| if (Offset.hasSymbolicOffset()) |
| return UnknownVal(); |
| const MemRegion *BaseRegion = MRegion->getBaseRegion(); |
| if (!BaseRegion) |
| return UnknownVal(); |
| |
| SValBuilder &SvalBuilder = C.getSValBuilder(); |
| NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( |
| Offset.getOffset() / C.getASTContext().getCharWidth()); |
| DefinedOrUnknownSVal ExtentInBytes = |
| getDynamicSize(State, BaseRegion, SvalBuilder); |
| |
| return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, |
| ExtentInBytes, OffsetInBytes, |
| SvalBuilder.getArrayIndexType()); |
| } |
| |
| SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE, |
| ProgramStateRef State, |
| CheckerContext &C) const { |
| SValBuilder &SvalBuilder = C.getSValBuilder(); |
| QualType ElementType = NE->getAllocatedType(); |
| ASTContext &AstContext = C.getASTContext(); |
| CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); |
| if (NE->isArray()) { |
| const Expr *SizeExpr = *NE->getArraySize(); |
| SVal ElementCount = C.getSVal(SizeExpr); |
| if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) { |
| // size in Bytes = ElementCountNL * TypeSize |
| return SvalBuilder.evalBinOp( |
| State, BO_Mul, *ElementCountNL, |
| SvalBuilder.makeArrayIndex(TypeSize.getQuantity()), |
| SvalBuilder.getArrayIndexType()); |
| } |
| } else { |
| // Create a concrete int whose size in bits and signedness is equal to |
| // ArrayIndexType. |
| llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType()) |
| .getQuantity() * |
| C.getASTContext().getCharWidth(), |
| TypeSize.getQuantity()); |
| return SvalBuilder.makeArrayIndex(I.getZExtValue()); |
| } |
| return UnknownVal(); |
| } |
| |
| void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE, |
| CheckerContext &C) const { |
| // Check only the default placement new. |
| if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator()) |
| return; |
| if (NE->getNumPlacementArgs() == 0) |
| return; |
| |
| ProgramStateRef State = C.getState(); |
| SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C); |
| const Expr *Place = NE->getPlacementArg(0); |
| SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C); |
| const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>(); |
| if (!SizeOfTargetCI) |
| return; |
| const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>(); |
| if (!SizeOfPlaceCI) |
| return; |
| |
| if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) { |
| if (ExplodedNode *N = C.generateErrorNode(State)) { |
| std::string Msg = std::string( |
| llvm::formatv("Storage provided to placement new is only {0} bytes, " |
| "whereas the allocated type requires {1} bytes", |
| SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue())); |
| |
| auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); |
| bugreporter::trackExpressionValue(N, Place, *R); |
| C.emitReport(std::move(R)); |
| return; |
| } |
| } |
| } |
| |
| void ento::registerPlacementNewChecker(CheckerManager &mgr) { |
| mgr.registerChecker<PlacementNewChecker>(); |
| } |
| |
| bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) { |
| return true; |
| } |