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
getExtentSizeOfPlace(const CXXNewExpr * NE,CheckerContext & C) const64 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
getExtentSizeOfNewTarget(const CXXNewExpr * NE,CheckerContext & C,bool & IsArray) const70 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
checkPlaceCapacityIsSufficient(const CXXNewExpr * NE,CheckerContext & C) const102 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
emitBadAlignReport(const Expr * P,CheckerContext & C,unsigned AllocatedTAlign,unsigned StorageTAlign) const152 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
getStorageAlign(CheckerContext & C,const ValueDecl * VD) const167 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
checkElementRegionAlign(const ElementRegion * R,CheckerContext & C,const Expr * P,unsigned AllocatedTAlign) const176 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
checkFieldRegionAlign(const FieldRegion * R,CheckerContext & C,const Expr * P,unsigned AllocatedTAlign) const235 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
isVarRegionAlignedProperly(const VarRegion * R,CheckerContext & C,const Expr * P,unsigned AllocatedTAlign) const260 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
checkPlaceIsAlignedProperly(const CXXNewExpr * NE,CheckerContext & C) const274 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
checkPreStmt(const CXXNewExpr * NE,CheckerContext & C) const295 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
registerPlacementNewChecker(CheckerManager & mgr)310 void ento::registerPlacementNewChecker(CheckerManager &mgr) {
311 mgr.registerChecker<PlacementNewChecker>();
312 }
313
shouldRegisterPlacementNewChecker(const CheckerManager & mgr)314 bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
315 return true;
316 }
317