17 #include "llvm/Support/FormatVariadic.h"
19 using namespace clang;
23 class PlacementNewChecker :
public Checker<check::PreStmt<CXXNewExpr>> {
25 void checkPreStmt(
const CXXNewExpr *
NE, CheckerContext &C)
const;
28 bool checkPlaceCapacityIsSufficient(
const CXXNewExpr *
NE,
29 CheckerContext &C)
const;
32 CheckerContext &C)
const;
36 SVal getExtentSizeOfNewTarget(
const CXXNewExpr *
NE, CheckerContext &C,
40 SVal getExtentSizeOfPlace(
const CXXNewExpr *
NE, CheckerContext &C)
const;
42 void emitBadAlignReport(
const Expr *
P, CheckerContext &C,
43 unsigned AllocatedTAlign,
44 unsigned StorageTAlign)
const;
45 unsigned getStorageAlign(CheckerContext &C,
const ValueDecl *VD)
const;
47 void checkElementRegionAlign(
const ElementRegion *R, CheckerContext &C,
48 const Expr *
P,
unsigned AllocatedTAlign)
const;
50 void checkFieldRegionAlign(
const FieldRegion *R, CheckerContext &C,
51 const Expr *
P,
unsigned AllocatedTAlign)
const;
53 bool isVarRegionAlignedProperly(
const VarRegion *R, CheckerContext &C,
55 unsigned AllocatedTAlign)
const;
57 BugType SBT{
this,
"Insufficient storage for placement new",
59 BugType ABT{
this,
"Bad align storage for placement new",
64 SVal PlacementNewChecker::getExtentSizeOfPlace(
const CXXNewExpr *
NE,
65 CheckerContext &C)
const {
66 const Expr *Place =
NE->getPlacementArg(0);
70 SVal PlacementNewChecker::getExtentSizeOfNewTarget(
const CXXNewExpr *
NE,
72 bool &IsArray)
const {
74 SValBuilder &SvalBuilder =
C.getSValBuilder();
81 const Expr *SizeExpr = *
NE->getArraySize();
82 SVal ElementCount =
C.getSVal(SizeExpr);
83 if (
auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
85 return SvalBuilder.evalBinOp(
86 State, BO_Mul, *ElementCountNL,
88 SvalBuilder.getArrayIndexType());
95 C.getASTContext().getCharWidth(),
97 return SvalBuilder.makeArrayIndex(I.getZExtValue());
102 bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
104 bool IsArrayTypeAllocated;
105 SVal SizeOfTarget = getExtentSizeOfNewTarget(
NE, C, IsArrayTypeAllocated);
106 SVal SizeOfPlace = getExtentSizeOfPlace(
NE, C);
107 const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
110 const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
114 if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
115 (IsArrayTypeAllocated &&
116 SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
117 if (ExplodedNode *N =
C.generateErrorNode(
C.getState())) {
120 if (IsArrayTypeAllocated &&
121 SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
123 "{0} bytes is possibly not enough for array allocation which "
124 "requires {1} bytes. Current overhead requires the size of {2} "
126 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
127 SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
128 else if (IsArrayTypeAllocated &&
129 SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
131 "Storage provided to placement new is only {0} bytes, "
132 "whereas the allocated array type requires more space for "
134 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
137 "Storage provided to placement new is only {0} bytes, "
138 "whereas the allocated type requires {1} bytes",
139 SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
141 auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
143 C.emitReport(std::move(R));
152 void PlacementNewChecker::emitBadAlignReport(
const Expr *
P, CheckerContext &C,
153 unsigned AllocatedTAlign,
154 unsigned StorageTAlign)
const {
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));
161 auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
163 C.emitReport(std::move(R));
167 unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
169 unsigned StorageTAlign =
C.getASTContext().getTypeAlign(VD->
getType());
171 StorageTAlign = SpecifiedAlignment;
173 return StorageTAlign /
C.getASTContext().getCharWidth();
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 {
182 const MemRegion *SuperRegion = R;
184 if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
185 SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
192 const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
193 if (!TheElementDeclRegion)
196 const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
200 unsigned BaseRegionAlign = 0;
203 if (TheElementDeclRegion->getDecl()->getMaxAlignment())
204 BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
206 BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
208 if (AllocatedTAlign > BaseRegionAlign) {
209 emitBadAlignReport(
P, C, AllocatedTAlign, BaseRegionAlign);
216 auto CheckElementRegionOffset = [
this, R, &
C,
P, AllocatedTAlign]() ->
void {
217 RegionOffset TheOffsetRegion = R->getAsOffset();
218 if (TheOffsetRegion.hasSymbolicOffset())
222 TheOffsetRegion.getOffset() /
C.getASTContext().getCharWidth();
223 unsigned AddressAlign =
Offset % AllocatedTAlign;
224 if (AddressAlign != 0) {
225 emitBadAlignReport(
P, C, AllocatedTAlign, AddressAlign);
230 if (IsBaseRegionAlignedProperly()) {
231 CheckElementRegionOffset();
235 void PlacementNewChecker::checkFieldRegionAlign(
236 const FieldRegion *R, CheckerContext &C,
const Expr *
P,
237 unsigned AllocatedTAlign)
const {
238 const MemRegion *BaseRegion = R->getBaseRegion();
242 if (
const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
243 if (isVarRegionAlignedProperly(TheVarRegion, C,
P, AllocatedTAlign)) {
247 RegionOffset
Offset = R->getAsOffset();
248 if (
Offset.hasSymbolicOffset())
252 Offset.getOffset() /
C.getASTContext().getCharWidth();
253 unsigned AddressAlign = OffsetValue % AllocatedTAlign;
254 if (AddressAlign != 0)
255 emitBadAlignReport(
P, C, AllocatedTAlign, AddressAlign);
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);
274 bool PlacementNewChecker::checkPlaceIsAlignedProperly(
const CXXNewExpr *
NE,
275 CheckerContext &C)
const {
276 const Expr *Place =
NE->getPlacementArg(0);
279 unsigned AllocatedTAlign =
C.getASTContext().getTypeAlign(AllocatedT) /
280 C.getASTContext().getCharWidth();
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);
295 void PlacementNewChecker::checkPreStmt(
const CXXNewExpr *
NE,
296 CheckerContext &C)
const {
298 if (!
NE->getOperatorNew()->isReservedGlobalPlacementOperator())
301 if (
NE->getNumPlacementArgs() == 0)
304 if (!checkPlaceCapacityIsSufficient(
NE, C))
307 checkPlaceIsAlignedProperly(
NE, C);
310 void ento::registerPlacementNewChecker(CheckerManager &mgr) {
311 mgr.registerChecker<PlacementNewChecker>();
314 bool ento::shouldRegisterPlacementNewChecker(
const CheckerManager &mgr) {