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