17#include "llvm/Support/FormatVariadic.h"
23class 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;
31 bool checkPlaceIsAlignedProperly(
const CXXNewExpr *NE,
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",
64SVal PlacementNewChecker::getExtentSizeOfPlace(
const CXXNewExpr *NE,
66 const Expr *Place =
NE->getPlacementArg(0);
70SVal PlacementNewChecker::getExtentSizeOfNewTarget(
const CXXNewExpr *NE,
72 bool &IsArray)
const {
74 SValBuilder &SvalBuilder =
C.getSValBuilder();
75 QualType ElementType =
NE->getAllocatedType();
76 ASTContext &AstContext =
C.getASTContext();
81 const Expr *SizeExpr = *
NE->getArraySize();
82 SVal ElementCount =
C.getSVal(SizeExpr);
83 if (
auto ElementCountNL = ElementCount.
getAs<NonLoc>()) {
86 State, BO_Mul, *ElementCountNL,
95 C.getASTContext().getCharWidth(),
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>();
110 const auto SizeOfPlaceCI = SizeOfPlace.
getAs<nonloc::ConcreteInt>();
114 if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue())) {
115 if (ExplodedNode *N =
C.generateErrorNode(
C.getState())) {
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());
121 auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
123 C.emitReport(std::move(R));
132void PlacementNewChecker::emitBadAlignReport(
const Expr *P, CheckerContext &
C,
133 unsigned AllocatedTAlign,
134 unsigned StorageTAlign)
const {
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));
141 auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
143 C.emitReport(std::move(R));
147unsigned PlacementNewChecker::getStorageAlign(CheckerContext &
C,
148 const ValueDecl *VD)
const {
149 unsigned StorageTAlign =
C.getASTContext().getTypeAlign(VD->
getType());
151 StorageTAlign = SpecifiedAlignment;
153 return StorageTAlign /
C.getASTContext().getCharWidth();
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 {
162 const MemRegion *SuperRegion = R;
164 if (SuperRegion->
getKind() == MemRegion::ElementRegionKind) {
172 const DeclRegion *TheElementDeclRegion = SuperRegion->
getAs<DeclRegion>();
173 if (!TheElementDeclRegion)
180 unsigned BaseRegionAlign = 0;
184 BaseRegionAlign = getStorageAlign(
C, TheElementDeclRegion->
getDecl());
186 BaseRegionAlign = getStorageAlign(
C, BaseDeclRegion->
getDecl());
188 if (AllocatedTAlign > BaseRegionAlign) {
189 emitBadAlignReport(P,
C, AllocatedTAlign, BaseRegionAlign);
196 auto CheckElementRegionOffset = [
this, R, &
C, P, AllocatedTAlign]() ->
void {
202 TheOffsetRegion.
getOffset() /
C.getASTContext().getCharWidth();
203 unsigned AddressAlign = Offset % AllocatedTAlign;
204 if (AddressAlign != 0) {
205 emitBadAlignReport(P,
C, AllocatedTAlign, AddressAlign);
210 if (IsBaseRegionAlignedProperly()) {
211 CheckElementRegionOffset();
215void PlacementNewChecker::checkFieldRegionAlign(
216 const FieldRegion *R, CheckerContext &
C,
const Expr *P,
217 unsigned AllocatedTAlign)
const {
222 if (
const VarRegion *TheVarRegion = BaseRegion->
getAs<VarRegion>()) {
223 if (isVarRegionAlignedProperly(TheVarRegion,
C, P, AllocatedTAlign)) {
232 Offset.
getOffset() /
C.getASTContext().getCharWidth();
233 unsigned AddressAlign = OffsetValue % AllocatedTAlign;
234 if (AddressAlign != 0)
235 emitBadAlignReport(P,
C, AllocatedTAlign, AddressAlign);
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);
254bool PlacementNewChecker::checkPlaceIsAlignedProperly(
const CXXNewExpr *NE,
255 CheckerContext &
C)
const {
256 const Expr *Place =
NE->getPlacementArg(0);
258 QualType AllocatedT =
NE->getAllocatedType();
259 unsigned AllocatedTAlign =
C.getASTContext().getTypeAlign(AllocatedT) /
260 C.getASTContext().getCharWidth();
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);
275void PlacementNewChecker::checkPreStmt(
const CXXNewExpr *NE,
276 CheckerContext &
C)
const {
278 if (!
NE->getOperatorNew()->isReservedGlobalPlacementOperator())
281 if (
NE->getNumPlacementArgs() == 0)
284 if (!checkPlaceCapacityIsSufficient(NE,
C))
287 checkPlaceIsAlignedProperly(NE,
C);
290void ento::registerPlacementNewChecker(CheckerManager &mgr) {
294bool ento::shouldRegisterPlacementNewChecker(
const CheckerManager &mgr) {
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)".
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
unsigned getMaxAlignment() const
getMaxAlignment - return the maximum alignment specified by attributes on this decl,...
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.
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
bool hasSymbolicOffset() const
int64_t getOffset() const
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.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
const MemRegion * getAsRegion() const
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.
const char *const MemoryError
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)
The JSON file list parser is used to communicate input to InstallAPI.
U cast(CodeGen::Address addr)