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