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"
10 class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
12 void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
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};
28 SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
29 ProgramStateRef State,
30 CheckerContext &C) const {
31 const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
34 RegionOffset Offset = MRegion->getAsOffset();
35 if (Offset.hasSymbolicOffset())
37 const MemRegion *BaseRegion = MRegion->getBaseRegion();
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);
47 return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
48 ExtentInBytes, OffsetInBytes,
49 SvalBuilder.getArrayIndexType());
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);
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());
70 // Create a concrete int whose size in bits and signedness is equal to
72 llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
74 C.getASTContext().getCharWidth(),
75 TypeSize.getQuantity());
76 return SvalBuilder.makeArrayIndex(I.getZExtValue());
81 void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
82 CheckerContext &C) const {
83 // Check only the default placement new.
84 if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
86 if (NE->getNumPlacementArgs() == 0)
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>();
96 const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
100 if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
101 if (ExplodedNode *N = C.generateErrorNode(State)) {
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());
107 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
108 bugreporter::trackExpressionValue(N, Place, *R);
109 C.emitReport(std::move(R));
115 void ento::registerPlacementNewChecker(CheckerManager &mgr) {
116 mgr.registerChecker<PlacementNewChecker>();
119 bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) {