Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
35269 views
1
//==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//
2
//
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
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file defines a check for misuse of the default placement new operator.
10
//
11
//===----------------------------------------------------------------------===//
12
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"
18
19
using namespace clang;
20
using namespace ento;
21
22
namespace {
23
class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
24
public:
25
void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
26
27
private:
28
bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,
29
CheckerContext &C) const;
30
31
bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
32
CheckerContext &C) const;
33
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,
37
bool &IsArray) const;
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;
41
42
void emitBadAlignReport(const Expr *P, CheckerContext &C,
43
unsigned AllocatedTAlign,
44
unsigned StorageTAlign) const;
45
unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;
46
47
void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,
48
const Expr *P, unsigned AllocatedTAlign) const;
49
50
void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,
51
const Expr *P, unsigned AllocatedTAlign) const;
52
53
bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,
54
const Expr *P,
55
unsigned AllocatedTAlign) const;
56
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};
61
};
62
} // namespace
63
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));
68
}
69
70
SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
71
CheckerContext &C,
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);
78
IsArray = false;
79
if (NE->isArray()) {
80
IsArray = true;
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());
89
}
90
} else {
91
// Create a concrete int whose size in bits and signedness is equal to
92
// ArrayIndexType.
93
llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
94
.getQuantity() *
95
C.getASTContext().getCharWidth(),
96
TypeSize.getQuantity());
97
return SvalBuilder.makeArrayIndex(I.getZExtValue());
98
}
99
return UnknownVal();
100
}
101
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>();
108
if (!SizeOfTargetCI)
109
return true;
110
const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
111
if (!SizeOfPlaceCI)
112
return true;
113
114
if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
115
(IsArrayTypeAllocated &&
116
SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
117
if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
118
std::string Msg;
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} "
125
"bytes",
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 "
133
"internal needs",
134
SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
135
else
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()));
140
141
auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
142
bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
143
C.emitReport(std::move(R));
144
145
return false;
146
}
147
}
148
149
return true;
150
}
151
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));
160
161
auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
162
bugreporter::trackExpressionValue(N, P, *R);
163
C.emitReport(std::move(R));
164
}
165
}
166
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;
172
173
return StorageTAlign / C.getASTContext().getCharWidth();
174
}
175
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;
183
while (true) {
184
if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
185
SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
186
continue;
187
}
188
189
break;
190
}
191
192
const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
193
if (!TheElementDeclRegion)
194
return false;
195
196
const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
197
if (!BaseDeclRegion)
198
return false;
199
200
unsigned BaseRegionAlign = 0;
201
// We must use alignment TheElementDeclRegion if it has its own alignment
202
// specifier
203
if (TheElementDeclRegion->getDecl()->getMaxAlignment())
204
BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
205
else
206
BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
207
208
if (AllocatedTAlign > BaseRegionAlign) {
209
emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
210
return false;
211
}
212
213
return true;
214
};
215
216
auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
217
RegionOffset TheOffsetRegion = R->getAsOffset();
218
if (TheOffsetRegion.hasSymbolicOffset())
219
return;
220
221
unsigned Offset =
222
TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
223
unsigned AddressAlign = Offset % AllocatedTAlign;
224
if (AddressAlign != 0) {
225
emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
226
return;
227
}
228
};
229
230
if (IsBaseRegionAlignedProperly()) {
231
CheckElementRegionOffset();
232
}
233
}
234
235
void PlacementNewChecker::checkFieldRegionAlign(
236
const FieldRegion *R, CheckerContext &C, const Expr *P,
237
unsigned AllocatedTAlign) const {
238
const MemRegion *BaseRegion = R->getBaseRegion();
239
if (!BaseRegion)
240
return;
241
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
246
// align.
247
RegionOffset Offset = R->getAsOffset();
248
if (Offset.hasSymbolicOffset())
249
return;
250
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);
256
}
257
}
258
}
259
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);
267
268
return false;
269
}
270
271
return true;
272
}
273
274
bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
275
CheckerContext &C) const {
276
const Expr *Place = NE->getPlacementArg(0);
277
278
QualType AllocatedT = NE->getAllocatedType();
279
unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
280
C.getASTContext().getCharWidth();
281
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);
290
}
291
292
return true;
293
}
294
295
void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
296
CheckerContext &C) const {
297
// Check only the default placement new.
298
if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
299
return;
300
301
if (NE->getNumPlacementArgs() == 0)
302
return;
303
304
if (!checkPlaceCapacityIsSufficient(NE, C))
305
return;
306
307
checkPlaceIsAlignedProperly(NE, C);
308
}
309
310
void ento::registerPlacementNewChecker(CheckerManager &mgr) {
311
mgr.registerChecker<PlacementNewChecker>();
312
}
313
314
bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
315
return true;
316
}
317
318