clang  10.0.0git
CheckPlacementNew.cpp
Go to the documentation of this file.
4 #include "llvm/Support/FormatVariadic.h"
5 
6 using namespace clang;
7 using namespace ento;
8 
9 namespace {
10 class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
11 public:
12  void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
13 
14 private:
15  // Returns the size of the target in a placement new expression.
16  // E.g. in "new (&s) long" it returns the size of `long`.
17  SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State,
18  CheckerContext &C) const;
19  // Returns the size of the place in a placement new expression.
20  // E.g. in "new (&s) long" it returns the size of `s`.
21  SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State,
22  CheckerContext &C) const;
23  BugType BT{this, "Insufficient storage for placement new",
25 };
26 } // namespace
27 
28 SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
30  CheckerContext &C) const {
31  const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
32  if (!MRegion)
33  return UnknownVal();
34  RegionOffset Offset = MRegion->getAsOffset();
35  if (Offset.hasSymbolicOffset())
36  return UnknownVal();
37  const MemRegion *BaseRegion = MRegion->getBaseRegion();
38  if (!BaseRegion)
39  return UnknownVal();
40 
41  SValBuilder &SvalBuilder = C.getSValBuilder();
42  NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
43  Offset.getOffset() / C.getASTContext().getCharWidth());
44  DefinedOrUnknownSVal ExtentInBytes =
45  BaseRegion->castAs<SubRegion>()->getExtent(SvalBuilder);
46 
47  return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
48  ExtentInBytes, OffsetInBytes,
49  SvalBuilder.getArrayIndexType());
50 }
51 
52 SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
53  ProgramStateRef State,
54  CheckerContext &C) const {
55  SValBuilder &SvalBuilder = C.getSValBuilder();
56  QualType ElementType = NE->getAllocatedType();
57  ASTContext &AstContext = C.getASTContext();
58  CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
59  if (NE->isArray()) {
60  const Expr *SizeExpr = *NE->getArraySize();
61  SVal ElementCount = C.getSVal(SizeExpr);
62  if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
63  // size in Bytes = ElementCountNL * TypeSize
64  return SvalBuilder.evalBinOp(
65  State, BO_Mul, *ElementCountNL,
66  SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
67  SvalBuilder.getArrayIndexType());
68  }
69  } else {
70  // Create a concrete int whose size in bits and signedness is equal to
71  // ArrayIndexType.
72  llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
73  .getQuantity() *
74  C.getASTContext().getCharWidth(),
75  TypeSize.getQuantity());
76  return SvalBuilder.makeArrayIndex(I.getZExtValue());
77  }
78  return UnknownVal();
79 }
80 
81 void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
82  CheckerContext &C) const {
83  // Check only the default placement new.
85  return;
86  if (NE->getNumPlacementArgs() == 0)
87  return;
88 
89  ProgramStateRef State = C.getState();
90  SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C);
91  const Expr *Place = NE->getPlacementArg(0);
92  SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C);
93  const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
94  if (!SizeOfTargetCI)
95  return;
96  const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
97  if (!SizeOfPlaceCI)
98  return;
99 
100  if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
101  if (ExplodedNode *N = C.generateErrorNode(State)) {
102  std::string Msg =
103  llvm::formatv("Storage provided to placement new is only {0} bytes, "
104  "whereas the allocated type requires {1} bytes",
105  SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
106 
107  auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
108  bugreporter::trackExpressionValue(N, Place, *R);
109  C.emitReport(std::move(R));
110  return;
111  }
112  }
113 }
114 
115 void ento::registerPlacementNewChecker(CheckerManager &mgr) {
116  mgr.registerChecker<PlacementNewChecker>();
117 }
118 
119 bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) {
120  return true;
121 }
bool isReservedGlobalPlacementOperator() const
Determines whether this operator new or delete is one of the reserved global placement operators: voi...
Definition: Decl.cpp:2961
A (possibly-)qualified type.
Definition: Type.h:654
FunctionDecl * getOperatorNew() const
Definition: ExprCXX.h:2218
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
unsigned getNumPlacementArgs() const
Definition: ExprCXX.h:2236
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:168
LineState State
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
Optional< Expr * > getArraySize()
Definition: ExprCXX.h:2225
CharUnits - This is an opaque type for sizes expressed in character units.
Definition: CharUnits.h:38
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition: CharUnits.h:179
unsigned Offset
Definition: Format.cpp:1827
This represents one expression.
Definition: Expr.h:108
Represents a new-expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)"...
Definition: ExprCXX.h:2100
QualType getAllocatedType() const
Definition: ExprCXX.h:2193
bool isArray() const
Definition: ExprCXX.h:2223
llvm::APInt APInt
Definition: Integral.h:27
Dataflow Directional Tag Classes.
bool NE(InterpState &S, CodePtr OpPC)
Definition: Interp.h:223
Expr * getPlacementArg(unsigned I)
Definition: ExprCXX.h:2245
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.