1 //==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a check for misuse of the default placement new operator.
11 //===----------------------------------------------------------------------===//
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
17 #include "llvm/Support/FormatVariadic.h"
19 using namespace clang
;
23 class PlacementNewChecker
: public Checker
<check::PreStmt
<CXXNewExpr
>> {
25 void checkPreStmt(const CXXNewExpr
*NE
, CheckerContext
&C
) const;
28 bool checkPlaceCapacityIsSufficient(const CXXNewExpr
*NE
,
29 CheckerContext
&C
) const;
31 bool checkPlaceIsAlignedProperly(const CXXNewExpr
*NE
,
32 CheckerContext
&C
) const;
34 // Returns the size of the target in a placement new expression.
35 // E.g. in "new (&s) long" it returns the size of `long`.
36 SVal
getExtentSizeOfNewTarget(const CXXNewExpr
*NE
, CheckerContext
&C
,
38 // Returns the size of the place in a placement new expression.
39 // E.g. in "new (&s) long" it returns the size of `s`.
40 SVal
getExtentSizeOfPlace(const CXXNewExpr
*NE
, CheckerContext
&C
) const;
42 void emitBadAlignReport(const Expr
*P
, CheckerContext
&C
,
43 unsigned AllocatedTAlign
,
44 unsigned StorageTAlign
) const;
45 unsigned getStorageAlign(CheckerContext
&C
, const ValueDecl
*VD
) const;
47 void checkElementRegionAlign(const ElementRegion
*R
, CheckerContext
&C
,
48 const Expr
*P
, unsigned AllocatedTAlign
) const;
50 void checkFieldRegionAlign(const FieldRegion
*R
, CheckerContext
&C
,
51 const Expr
*P
, unsigned AllocatedTAlign
) const;
53 bool isVarRegionAlignedProperly(const VarRegion
*R
, CheckerContext
&C
,
55 unsigned AllocatedTAlign
) const;
57 BugType SBT
{this, "Insufficient storage for placement new",
58 categories::MemoryError
};
59 BugType ABT
{this, "Bad align storage for placement new",
60 categories::MemoryError
};
64 SVal
PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr
*NE
,
65 CheckerContext
&C
) const {
66 const Expr
*Place
= NE
->getPlacementArg(0);
67 return getDynamicExtentWithOffset(C
.getState(), C
.getSVal(Place
));
70 SVal
PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr
*NE
,
72 bool &IsArray
) const {
73 ProgramStateRef State
= C
.getState();
74 SValBuilder
&SvalBuilder
= C
.getSValBuilder();
75 QualType ElementType
= NE
->getAllocatedType();
76 ASTContext
&AstContext
= C
.getASTContext();
77 CharUnits TypeSize
= AstContext
.getTypeSizeInChars(ElementType
);
81 const Expr
*SizeExpr
= *NE
->getArraySize();
82 SVal ElementCount
= C
.getSVal(SizeExpr
);
83 if (auto ElementCountNL
= ElementCount
.getAs
<NonLoc
>()) {
84 // size in Bytes = ElementCountNL * TypeSize
85 return SvalBuilder
.evalBinOp(
86 State
, BO_Mul
, *ElementCountNL
,
87 SvalBuilder
.makeArrayIndex(TypeSize
.getQuantity()),
88 SvalBuilder
.getArrayIndexType());
91 // Create a concrete int whose size in bits and signedness is equal to
93 llvm::APInt
I(AstContext
.getTypeSizeInChars(SvalBuilder
.getArrayIndexType())
95 C
.getASTContext().getCharWidth(),
96 TypeSize
.getQuantity());
97 return SvalBuilder
.makeArrayIndex(I
.getZExtValue());
102 bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
103 const CXXNewExpr
*NE
, CheckerContext
&C
) const {
104 bool IsArrayTypeAllocated
;
105 SVal SizeOfTarget
= getExtentSizeOfNewTarget(NE
, C
, IsArrayTypeAllocated
);
106 SVal SizeOfPlace
= getExtentSizeOfPlace(NE
, C
);
107 const auto SizeOfTargetCI
= SizeOfTarget
.getAs
<nonloc::ConcreteInt
>();
110 const auto SizeOfPlaceCI
= SizeOfPlace
.getAs
<nonloc::ConcreteInt
>();
114 if ((SizeOfPlaceCI
->getValue() < SizeOfTargetCI
->getValue()) ||
115 (IsArrayTypeAllocated
&&
116 SizeOfPlaceCI
->getValue() >= SizeOfTargetCI
->getValue())) {
117 if (ExplodedNode
*N
= C
.generateErrorNode(C
.getState())) {
119 // TODO: use clang constant
120 if (IsArrayTypeAllocated
&&
121 SizeOfPlaceCI
->getValue() > SizeOfTargetCI
->getValue())
122 Msg
= std::string(llvm::formatv(
123 "{0} bytes is possibly not enough for array allocation which "
124 "requires {1} bytes. Current overhead requires the size of {2} "
126 SizeOfPlaceCI
->getValue(), SizeOfTargetCI
->getValue(),
127 SizeOfPlaceCI
->getValue() - SizeOfTargetCI
->getValue()));
128 else if (IsArrayTypeAllocated
&&
129 SizeOfPlaceCI
->getValue() == SizeOfTargetCI
->getValue())
130 Msg
= std::string(llvm::formatv(
131 "Storage provided to placement new is only {0} bytes, "
132 "whereas the allocated array type requires more space for "
134 SizeOfPlaceCI
->getValue(), SizeOfTargetCI
->getValue()));
136 Msg
= std::string(llvm::formatv(
137 "Storage provided to placement new is only {0} bytes, "
138 "whereas the allocated type requires {1} bytes",
139 SizeOfPlaceCI
->getValue(), SizeOfTargetCI
->getValue()));
141 auto R
= std::make_unique
<PathSensitiveBugReport
>(SBT
, Msg
, N
);
142 bugreporter::trackExpressionValue(N
, NE
->getPlacementArg(0), *R
);
143 C
.emitReport(std::move(R
));
152 void PlacementNewChecker::emitBadAlignReport(const Expr
*P
, CheckerContext
&C
,
153 unsigned AllocatedTAlign
,
154 unsigned StorageTAlign
) const {
155 ProgramStateRef State
= C
.getState();
156 if (ExplodedNode
*N
= C
.generateErrorNode(State
)) {
157 std::string
Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
158 "allocated type is aligned to {1} bytes",
159 StorageTAlign
, AllocatedTAlign
));
161 auto R
= std::make_unique
<PathSensitiveBugReport
>(ABT
, Msg
, N
);
162 bugreporter::trackExpressionValue(N
, P
, *R
);
163 C
.emitReport(std::move(R
));
167 unsigned PlacementNewChecker::getStorageAlign(CheckerContext
&C
,
168 const ValueDecl
*VD
) const {
169 unsigned StorageTAlign
= C
.getASTContext().getTypeAlign(VD
->getType());
170 if (unsigned SpecifiedAlignment
= VD
->getMaxAlignment())
171 StorageTAlign
= SpecifiedAlignment
;
173 return StorageTAlign
/ C
.getASTContext().getCharWidth();
176 void PlacementNewChecker::checkElementRegionAlign(
177 const ElementRegion
*R
, CheckerContext
&C
, const Expr
*P
,
178 unsigned AllocatedTAlign
) const {
179 auto IsBaseRegionAlignedProperly
= [this, R
, &C
, P
,
180 AllocatedTAlign
]() -> bool {
181 // Unwind nested ElementRegion`s to get the type.
182 const MemRegion
*SuperRegion
= R
;
184 if (SuperRegion
->getKind() == MemRegion::ElementRegionKind
) {
185 SuperRegion
= cast
<SubRegion
>(SuperRegion
)->getSuperRegion();
192 const DeclRegion
*TheElementDeclRegion
= SuperRegion
->getAs
<DeclRegion
>();
193 if (!TheElementDeclRegion
)
196 const DeclRegion
*BaseDeclRegion
= R
->getBaseRegion()->getAs
<DeclRegion
>();
200 unsigned BaseRegionAlign
= 0;
201 // We must use alignment TheElementDeclRegion if it has its own alignment
203 if (TheElementDeclRegion
->getDecl()->getMaxAlignment())
204 BaseRegionAlign
= getStorageAlign(C
, TheElementDeclRegion
->getDecl());
206 BaseRegionAlign
= getStorageAlign(C
, BaseDeclRegion
->getDecl());
208 if (AllocatedTAlign
> BaseRegionAlign
) {
209 emitBadAlignReport(P
, C
, AllocatedTAlign
, BaseRegionAlign
);
216 auto CheckElementRegionOffset
= [this, R
, &C
, P
, AllocatedTAlign
]() -> void {
217 RegionOffset TheOffsetRegion
= R
->getAsOffset();
218 if (TheOffsetRegion
.hasSymbolicOffset())
222 TheOffsetRegion
.getOffset() / C
.getASTContext().getCharWidth();
223 unsigned AddressAlign
= Offset
% AllocatedTAlign
;
224 if (AddressAlign
!= 0) {
225 emitBadAlignReport(P
, C
, AllocatedTAlign
, AddressAlign
);
230 if (IsBaseRegionAlignedProperly()) {
231 CheckElementRegionOffset();
235 void PlacementNewChecker::checkFieldRegionAlign(
236 const FieldRegion
*R
, CheckerContext
&C
, const Expr
*P
,
237 unsigned AllocatedTAlign
) const {
238 const MemRegion
*BaseRegion
= R
->getBaseRegion();
242 if (const VarRegion
*TheVarRegion
= BaseRegion
->getAs
<VarRegion
>()) {
243 if (isVarRegionAlignedProperly(TheVarRegion
, C
, P
, AllocatedTAlign
)) {
244 // We've checked type align but, unless FieldRegion
245 // offset is zero, we also need to check its own
247 RegionOffset Offset
= R
->getAsOffset();
248 if (Offset
.hasSymbolicOffset())
251 int64_t OffsetValue
=
252 Offset
.getOffset() / C
.getASTContext().getCharWidth();
253 unsigned AddressAlign
= OffsetValue
% AllocatedTAlign
;
254 if (AddressAlign
!= 0)
255 emitBadAlignReport(P
, C
, AllocatedTAlign
, AddressAlign
);
260 bool PlacementNewChecker::isVarRegionAlignedProperly(
261 const VarRegion
*R
, CheckerContext
&C
, const Expr
*P
,
262 unsigned AllocatedTAlign
) const {
263 const VarDecl
*TheVarDecl
= R
->getDecl();
264 unsigned StorageTAlign
= getStorageAlign(C
, TheVarDecl
);
265 if (AllocatedTAlign
> StorageTAlign
) {
266 emitBadAlignReport(P
, C
, AllocatedTAlign
, StorageTAlign
);
274 bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr
*NE
,
275 CheckerContext
&C
) const {
276 const Expr
*Place
= NE
->getPlacementArg(0);
278 QualType AllocatedT
= NE
->getAllocatedType();
279 unsigned AllocatedTAlign
= C
.getASTContext().getTypeAlign(AllocatedT
) /
280 C
.getASTContext().getCharWidth();
282 SVal PlaceVal
= C
.getSVal(Place
);
283 if (const MemRegion
*MRegion
= PlaceVal
.getAsRegion()) {
284 if (const ElementRegion
*TheElementRegion
= MRegion
->getAs
<ElementRegion
>())
285 checkElementRegionAlign(TheElementRegion
, C
, Place
, AllocatedTAlign
);
286 else if (const FieldRegion
*TheFieldRegion
= MRegion
->getAs
<FieldRegion
>())
287 checkFieldRegionAlign(TheFieldRegion
, C
, Place
, AllocatedTAlign
);
288 else if (const VarRegion
*TheVarRegion
= MRegion
->getAs
<VarRegion
>())
289 isVarRegionAlignedProperly(TheVarRegion
, C
, Place
, AllocatedTAlign
);
295 void PlacementNewChecker::checkPreStmt(const CXXNewExpr
*NE
,
296 CheckerContext
&C
) const {
297 // Check only the default placement new.
298 if (!NE
->getOperatorNew()->isReservedGlobalPlacementOperator())
301 if (NE
->getNumPlacementArgs() == 0)
304 if (!checkPlaceCapacityIsSufficient(NE
, C
))
307 checkPlaceIsAlignedProperly(NE
, C
);
310 void ento::registerPlacementNewChecker(CheckerManager
&mgr
) {
311 mgr
.registerChecker
<PlacementNewChecker
>();
314 bool ento::shouldRegisterPlacementNewChecker(const CheckerManager
&mgr
) {