xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp (revision 62cfcf62f627e5093fb37026a6d8c98e4d2ef04c)
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