clang  14.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 
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",
59  BugType ABT{this, "Bad align storage for placement new",
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);
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 }
clang::ASTContext::getTypeSizeInChars
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
Definition: ASTContext.cpp:2450
clang::interp::APInt
llvm::APInt APInt
Definition: Integral.h:27
DynamicExtent.h
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:673
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:54
clang::CXXNewExpr
Represents a new-expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)".
Definition: ExprCXX.h:2139
clang::ento::getDynamicExtentWithOffset
SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV)
Get the dynamic extent for a symbolic value that represents a buffer.
Offset
unsigned Offset
Definition: Format.cpp:2335
BuiltinCheckerRegistration.h
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:212
clang::VarDecl
Represents a variable declaration or definition.
Definition: Decl.h:876
clang::ento::bugreporter::trackExpressionValue
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.
clang::ento::categories::MemoryError
const char *const MemoryError
Definition: CommonBugCategories.cpp:20
P
StringRef P
Definition: ASTMatchersInternal.cpp:563
BugType.h
clang::ValueDecl
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:676
State
LineState State
Definition: UnwrappedLineFormatter.cpp:986
CheckerContext.h
clang::interp::NE
bool NE(InterpState &S, CodePtr OpPC)
Definition: Interp.h:225
clang
Definition: CalledOnceCheck.h:17
clang::CharUnits
CharUnits - This is an opaque type for sizes expressed in character units.
Definition: CharUnits.h:38
clang::ValueDecl::getType
QualType getType() const
Definition: Decl.h:687
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::Decl::getMaxAlignment
unsigned getMaxAlignment() const
getMaxAlignment - return the maximum alignment specified by attributes on this decl,...
Definition: DeclBase.cpp:428
clang::CharUnits::getQuantity
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition: CharUnits.h:179