This patch introduces a way to store the size.
Reviewed By: NoQ
Differential Revision: https://reviews.llvm.org/D69726
namespace clang {
namespace ento {
-/// Get the stored dynamic size for the region \p MR.
+/// \returns The stored dynamic size for the region \p MR.
DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR,
SValBuilder &SVB);
-/// Get the stored element count of the region \p MR.
+/// \returns The element size of the type \p Ty.
+DefinedOrUnknownSVal getElementSize(QualType Ty, SValBuilder &SVB);
+
+/// \returns The stored element count of the region \p MR.
DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
const MemRegion *MR,
- SValBuilder &SVB,
- QualType ElementTy);
+ SValBuilder &SVB, QualType Ty);
+
+/// Set the dynamic size \p Size of the region \p MR.
+ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR,
+ DefinedOrUnknownSVal Size, SValBuilder &SVB);
/// Get the dynamic size for a symbolic value that represents a buffer. If
/// there is an offsetting to the underlying buffer we consider that too.
///
/// char *bufptr;
/// (bufptr) // size is unknown
-SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV);
+SVal getDynamicSizeWithOffset(ProgramStateRef State, SVal BufV);
} // namespace ento
} // namespace clang
if (Size.isUndef())
return true; // Return true to model purity.
- SValBuilder& svalBuilder = C.getSValBuilder();
- DefinedOrUnknownSVal DynSize = getDynamicSize(state, R, svalBuilder);
- DefinedOrUnknownSVal DynSizeMatchesSizeArg =
- svalBuilder.evalEQ(state, DynSize, Size.castAs<DefinedOrUnknownSVal>());
- state = state->assume(DynSizeMatchesSizeArg, true);
- assert(state && "The region should not have any previous constraints");
+ state = setDynamicSize(state, R, Size.castAs<DefinedOrUnknownSVal>(),
+ C.getSValBuilder());
C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
return true;
ProgramStateRef State,
AllocationFamily Family);
- LLVM_NODISCARD
- static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
- ProgramStateRef State, SVal Target);
-
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
LLVM_NODISCARD
// existing binding.
SVal Target = Call.getObjectUnderConstruction();
State = MallocUpdateRefState(C, NE, State, Family, Target);
- State = addExtentSize(C, NE, State, Target);
State = ProcessZeroAllocCheck(Call, 0, State, Target);
return State;
}
}
}
-// Sets the extent value of the MemRegion allocated by
-// new expression NE to its size in Bytes.
-//
-ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
- const CXXNewExpr *NE,
- ProgramStateRef State,
- SVal Target) {
- if (!State)
- return nullptr;
- SValBuilder &svalBuilder = C.getSValBuilder();
- SVal ElementCount;
- const SubRegion *Region;
- if (NE->isArray()) {
- const Expr *SizeExpr = *NE->getArraySize();
- ElementCount = C.getSVal(SizeExpr);
- // Store the extent size for the (symbolic)region
- // containing the elements.
- Region = Target.getAsRegion()
- ->castAs<SubRegion>()
- ->StripCasts()
- ->castAs<SubRegion>();
- } else {
- ElementCount = svalBuilder.makeIntVal(1, true);
- Region = Target.getAsRegion()->castAs<SubRegion>();
- }
-
- // Set the region's extent equal to the Size in Bytes.
- QualType ElementType = NE->getAllocatedType();
- ASTContext &AstContext = C.getASTContext();
- CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
-
- if (ElementCount.getAs<NonLoc>()) {
- DefinedOrUnknownSVal DynSize = getDynamicSize(State, Region, svalBuilder);
-
- // size in Bytes = ElementCount*TypeSize
- SVal SizeInBytes = svalBuilder.evalBinOpNN(
- State, BO_Mul, ElementCount.castAs<NonLoc>(),
- svalBuilder.makeArrayIndex(TypeSize.getQuantity()),
- svalBuilder.getArrayIndexType());
- DefinedOrUnknownSVal DynSizeMatchesSize = svalBuilder.evalEQ(
- State, DynSize, SizeInBytes.castAs<DefinedOrUnknownSVal>());
- State = State->assume(DynSizeMatchesSize, true);
- }
- return State;
-}
-
static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
// If the first selector piece is one of the names below, assume that the
// object takes ownership of the memory, promising to eventually deallocate it
// Fill the region with the initialization value.
State = State->bindDefaultInitial(RetVal, Init, LCtx);
- // Set the region's extent equal to the Size parameter.
- const SymbolicRegion *R =
- dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());
- if (!R)
- return nullptr;
- if (Optional<DefinedOrUnknownSVal> DefinedSize =
- Size.getAs<DefinedOrUnknownSVal>()) {
- DefinedOrUnknownSVal DynSize = getDynamicSize(State, R, svalBuilder);
-
- DefinedOrUnknownSVal DynSizeMatchesSize =
- svalBuilder.evalEQ(State, DynSize, *DefinedSize);
-
- State = State->assume(DynSizeMatchesSize, true);
- assert(State);
- }
+ // Set the region's extent.
+ State = setDynamicSize(State, RetVal.getAsRegion(),
+ Size.castAs<DefinedOrUnknownSVal>(), svalBuilder);
return MallocUpdateRefState(C, CE, State, Family);
}
return;
}
- // VLASizeChecker is responsible for defining the extent of the array being
- // declared. We do this by multiplying the array length by the element size,
- // then matching that with the array region's extent symbol.
-
+ // VLASizeChecker is responsible for defining the extent of the array.
if (VD) {
- // Assume that the array's size matches the region size.
- const LocationContext *LC = C.getLocationContext();
- DefinedOrUnknownSVal DynSize =
- getDynamicSize(State, State->getRegion(VD, LC), SVB);
-
- DefinedOrUnknownSVal SizeIsKnown = SVB.evalEQ(State, DynSize, *ArraySizeNL);
- State = State->assume(SizeIsKnown, true);
-
- // Assume should not fail at this point.
- assert(State);
+ State = setDynamicSize(State, State->getRegion(VD, C.getLocationContext()),
+ ArraySize.castAs<DefinedOrUnknownSVal>(), SVB);
}
// Remember our assumptions!
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicSizeMap, const clang::ento::MemRegion *,
+ clang::ento::DefinedOrUnknownSVal)
+
namespace clang {
namespace ento {
DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR,
SValBuilder &SVB) {
+ MR = MR->StripCasts();
+
+ if (const DefinedOrUnknownSVal *Size = State->get<DynamicSizeMap>(MR))
+ return *Size;
+
return MR->getMemRegionManager().getStaticSize(MR, SVB);
}
+DefinedOrUnknownSVal getElementSize(QualType Ty, SValBuilder &SVB) {
+ return SVB.makeIntVal(SVB.getContext().getTypeSizeInChars(Ty).getQuantity(),
+ SVB.getArrayIndexType());
+}
+
DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
const MemRegion *MR,
SValBuilder &SVB,
QualType ElementTy) {
- MemRegionManager &MemMgr = MR->getMemRegionManager();
- ASTContext &Ctx = MemMgr.getContext();
+ MR = MR->StripCasts();
DefinedOrUnknownSVal Size = getDynamicSize(State, MR, SVB);
- SVal ElementSizeV = SVB.makeIntVal(
- Ctx.getTypeSizeInChars(ElementTy).getQuantity(), SVB.getArrayIndexType());
+ SVal ElementSize = getElementSize(ElementTy, SVB);
- SVal DivisionV =
- SVB.evalBinOp(State, BO_Div, Size, ElementSizeV, SVB.getArrayIndexType());
+ SVal ElementCount =
+ SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType());
- return DivisionV.castAs<DefinedOrUnknownSVal>();
+ return ElementCount.castAs<DefinedOrUnknownSVal>();
}
-SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV) {
+SVal getDynamicSizeWithOffset(ProgramStateRef State, SVal BufV) {
SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
const MemRegion *MRegion = BufV.getAsRegion();
if (!MRegion)
SvalBuilder.getArrayIndexType());
}
+ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR,
+ DefinedOrUnknownSVal Size, SValBuilder &SVB) {
+ MR = MR->StripCasts();
+
+ if (Size.isUnknown())
+ return State;
+
+ return State->set<DynamicSizeMap>(MR->StripCasts(), Size);
+}
+
} // namespace ento
} // namespace clang
#include "clang/Analysis/ConstructionContext.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
// See if we need to conjure a heap pointer instead of
// a regular unknown pointer.
- bool IsHeapPointer = false;
- if (const auto *CNE = dyn_cast<CXXNewExpr>(E))
- if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
- // FIXME: Delegate this to evalCall in MallocChecker?
- IsHeapPointer = true;
+ const auto *CNE = dyn_cast<CXXNewExpr>(E);
+ if (CNE && CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
+ R = svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count);
+ const MemRegion *MR = R.getAsRegion()->StripCasts();
+
+ // Store the extent of the allocated object(s).
+ SVal ElementCount;
+ if (const Expr *SizeExpr = CNE->getArraySize().getValueOr(nullptr)) {
+ ElementCount = State->getSVal(SizeExpr, LCtx);
+ } else {
+ ElementCount = svalBuilder.makeIntVal(1, /*IsUnsigned=*/true);
}
- R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count)
- : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy,
- Count);
+ SVal ElementSize = getElementSize(CNE->getAllocatedType(), svalBuilder);
+
+ SVal Size =
+ svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize,
+ svalBuilder.getArrayIndexType());
+
+ State = setDynamicSize(State, MR, Size.castAs<DefinedOrUnknownSVal>(),
+ svalBuilder);
+ } else {
+ R = svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count);
+ }
}
return State->BindExpr(E, LCtx, R);
}
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
// MemRegionManager methods.
//===----------------------------------------------------------------------===//
-static DefinedOrUnknownSVal getTypeSize(QualType Ty, ASTContext &Ctx,
- SValBuilder &SVB) {
- CharUnits Size = Ctx.getTypeSizeInChars(Ty);
- QualType SizeTy = SVB.getArrayIndexType();
- return SVB.makeIntVal(Size.getQuantity(), SizeTy);
-}
-
DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
SValBuilder &SVB) const {
const auto *SR = cast<SubRegion>(MR);
if (Ty->isIncompleteType())
return UnknownVal();
- return getTypeSize(Ty, Ctx, SVB);
+ return getElementSize(Ty, SVB);
}
case MemRegion::FieldRegionKind: {
// Force callers to deal with bitfields explicitly.
return UnknownVal();
QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
- DefinedOrUnknownSVal Size = getTypeSize(Ty, Ctx, SVB);
+ DefinedOrUnknownSVal Size = getElementSize(Ty, SVB);
// A zero-length array at the end of a struct often stands for dynamically
// allocated extra memory.
int *x = new int[ext];
clang_analyzer_explain(x); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of heap segment that starts at symbol of type 'int \*' conjured at statement 'new int \[ext\]'$}}}}
// Sic! What gets computed is the extent of the element-region.
- clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^signed 32-bit integer '4'$}}}}
+ clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^\(argument 'ext'\) \* 4$}}}}
delete[] x;
}