clang 22.0.0git
CheckPlacementNew.cpp
Go to the documentation of this file.
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
17#include "llvm/Support/FormatVariadic.h"
18
19using namespace clang;
20using namespace ento;
21
22namespace {
23class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
24public:
25 void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
26
27private:
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",
59 BugType ABT{this, "Bad align storage for placement new",
61};
62} // namespace
63
64SVal 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
70SVal 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
102bool 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 if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
116 std::string Msg =
117 llvm::formatv("Storage provided to placement new is only {0} bytes, "
118 "whereas the allocated type requires {1} bytes",
119 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
120
121 auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
122 bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
123 C.emitReport(std::move(R));
124
125 return false;
126 }
127 }
128
129 return true;
130}
131
132void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,
133 unsigned AllocatedTAlign,
134 unsigned StorageTAlign) const {
135 ProgramStateRef State = C.getState();
136 if (ExplodedNode *N = C.generateErrorNode(State)) {
137 std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
138 "allocated type is aligned to {1} bytes",
139 StorageTAlign, AllocatedTAlign));
140
141 auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
143 C.emitReport(std::move(R));
144 }
145}
146
147unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
148 const ValueDecl *VD) const {
149 unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());
150 if (unsigned SpecifiedAlignment = VD->getMaxAlignment())
151 StorageTAlign = SpecifiedAlignment;
152
153 return StorageTAlign / C.getASTContext().getCharWidth();
154}
155
156void PlacementNewChecker::checkElementRegionAlign(
157 const ElementRegion *R, CheckerContext &C, const Expr *P,
158 unsigned AllocatedTAlign) const {
159 auto IsBaseRegionAlignedProperly = [this, R, &C, P,
160 AllocatedTAlign]() -> bool {
161 // Unwind nested ElementRegion`s to get the type.
162 const MemRegion *SuperRegion = R;
163 while (true) {
164 if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
165 SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
166 continue;
167 }
168
169 break;
170 }
171
172 const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
173 if (!TheElementDeclRegion)
174 return false;
175
176 const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
177 if (!BaseDeclRegion)
178 return false;
179
180 unsigned BaseRegionAlign = 0;
181 // We must use alignment TheElementDeclRegion if it has its own alignment
182 // specifier
183 if (TheElementDeclRegion->getDecl()->getMaxAlignment())
184 BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
185 else
186 BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
187
188 if (AllocatedTAlign > BaseRegionAlign) {
189 emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
190 return false;
191 }
192
193 return true;
194 };
195
196 auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
197 RegionOffset TheOffsetRegion = R->getAsOffset();
198 if (TheOffsetRegion.hasSymbolicOffset())
199 return;
200
201 unsigned Offset =
202 TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
203 unsigned AddressAlign = Offset % AllocatedTAlign;
204 if (AddressAlign != 0) {
205 emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
206 return;
207 }
208 };
209
210 if (IsBaseRegionAlignedProperly()) {
211 CheckElementRegionOffset();
212 }
213}
214
215void PlacementNewChecker::checkFieldRegionAlign(
216 const FieldRegion *R, CheckerContext &C, const Expr *P,
217 unsigned AllocatedTAlign) const {
218 const MemRegion *BaseRegion = R->getBaseRegion();
219 if (!BaseRegion)
220 return;
221
222 if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
223 if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {
224 // We've checked type align but, unless FieldRegion
225 // offset is zero, we also need to check its own
226 // align.
227 RegionOffset Offset = R->getAsOffset();
228 if (Offset.hasSymbolicOffset())
229 return;
230
231 int64_t OffsetValue =
232 Offset.getOffset() / C.getASTContext().getCharWidth();
233 unsigned AddressAlign = OffsetValue % AllocatedTAlign;
234 if (AddressAlign != 0)
235 emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
236 }
237 }
238}
239
240bool PlacementNewChecker::isVarRegionAlignedProperly(
241 const VarRegion *R, CheckerContext &C, const Expr *P,
242 unsigned AllocatedTAlign) const {
243 const VarDecl *TheVarDecl = R->getDecl();
244 unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);
245 if (AllocatedTAlign > StorageTAlign) {
246 emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);
247
248 return false;
249 }
250
251 return true;
252}
253
254bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
255 CheckerContext &C) const {
256 const Expr *Place = NE->getPlacementArg(0);
257
258 QualType AllocatedT = NE->getAllocatedType();
259 unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
260 C.getASTContext().getCharWidth();
261
262 SVal PlaceVal = C.getSVal(Place);
263 if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {
264 if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())
265 checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);
266 else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())
267 checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);
268 else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())
269 isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);
270 }
271
272 return true;
273}
274
275void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
276 CheckerContext &C) const {
277 // Check only the default placement new.
278 if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
279 return;
280
281 if (NE->getNumPlacementArgs() == 0)
282 return;
283
284 if (!checkPlaceCapacityIsSufficient(NE, C))
285 return;
286
287 checkPlaceIsAlignedProperly(NE, C);
288}
289
290void ento::registerPlacementNewChecker(CheckerManager &mgr) {
291 mgr.registerChecker<PlacementNewChecker>();
292}
293
294bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
295 return true;
296}
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
Represents a new-expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)".
Definition ExprCXX.h:2349
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
unsigned getMaxAlignment() const
getMaxAlignment - return the maximum alignment specified by attributes on this decl,...
Definition DeclBase.cpp:538
QualType getType() const
Definition Decl.h:722
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:553
virtual const ValueDecl * getDecl() const =0
RegionOffset getAsOffset() const
Compute the offset within the top level memory object.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
const RegionTy * getAs() const
Definition MemRegion.h:1416
Kind getKind() const
Definition MemRegion.h:203
bool hasSymbolicOffset() const
Definition MemRegion.h:83
int64_t getOffset() const
Definition MemRegion.h:85
NonLoc makeArrayIndex(uint64_t idx)
QualType getArrayIndexType() const
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:56
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:87
const MemRegion * getAsRegion() const
Definition SVals.cpp:119
const VarDecl * getDecl() const override=0
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV)
Get the dynamic extent for a symbolic value that represents a buffer.
bool NE(InterpState &S, CodePtr OpPC)
Definition Interp.h:1260
The JSON file list parser is used to communicate input to InstallAPI.
U cast(CodeGen::Address addr)
Definition Address.h:327
long int64_t