diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h @@ -22,15 +22,21 @@ 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. @@ -45,7 +51,7 @@ /// /// char *bufptr; /// (bufptr) // size is unknown -SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV); +SVal getDynamicSizeWithOffset(ProgramStateRef State, SVal BufV); } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -92,12 +92,8 @@ 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()); - state = state->assume(DynSizeMatchesSizeArg, true); - assert(state && "The region should not have any previous constraints"); + state = setDynamicSize(state, R, Size.castAs(), + C.getSValBuilder()); C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); return true; diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -509,10 +509,6 @@ 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 @@ -1424,7 +1420,6 @@ // 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; } @@ -1439,52 +1434,6 @@ } } -// 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() - ->StripCasts() - ->castAs(); - } else { - ElementCount = svalBuilder.makeIntVal(1, true); - Region = Target.getAsRegion()->castAs(); - } - - // 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()) { - DefinedOrUnknownSVal DynSize = getDynamicSize(State, Region, svalBuilder); - - // size in Bytes = ElementCount*TypeSize - SVal SizeInBytes = svalBuilder.evalBinOpNN( - State, BO_Mul, ElementCount.castAs(), - svalBuilder.makeArrayIndex(TypeSize.getQuantity()), - svalBuilder.getArrayIndexType()); - DefinedOrUnknownSVal DynSizeMatchesSize = svalBuilder.evalEQ( - State, DynSize, SizeInBytes.castAs()); - 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 @@ -1588,21 +1537,9 @@ // 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(RetVal.getAsRegion()); - if (!R) - return nullptr; - if (Optional DefinedSize = - Size.getAs()) { - 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(), svalBuilder); return MallocUpdateRefState(C, CE, State, Family); } diff --git a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -285,21 +285,10 @@ 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(), SVB); } // Remember our assumptions! diff --git a/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp b/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp --- a/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp +++ b/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp @@ -19,32 +19,43 @@ #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(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(); + return ElementCount.castAs(); } -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) @@ -67,5 +78,15 @@ SvalBuilder.getArrayIndexType()); } +ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR, + DefinedOrUnknownSVal Size, SValBuilder &SVB) { + MR = MR->StripCasts(); + + if (Size.isUnknown()) + return State; + + return State->set(MR->StripCasts(), Size); +} + } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -18,6 +18,7 @@ #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" @@ -689,16 +690,30 @@ // See if we need to conjure a heap pointer instead of // a regular unknown pointer. - bool IsHeapPointer = false; - if (const auto *CNE = dyn_cast(E)) - if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) { - // FIXME: Delegate this to evalCall in MallocChecker? - IsHeapPointer = true; + const auto *CNE = dyn_cast(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(), + svalBuilder); + } else { + R = svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); + } } return State->BindExpr(E, LCtx, R); } diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -28,6 +28,7 @@ #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" @@ -729,13 +730,6 @@ // 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(MR); @@ -766,7 +760,7 @@ if (Ty->isIncompleteType()) return UnknownVal(); - return getTypeSize(Ty, Ctx, SVB); + return getElementSize(Ty, SVB); } case MemRegion::FieldRegionKind: { // Force callers to deal with bitfields explicitly. @@ -774,7 +768,7 @@ return UnknownVal(); QualType Ty = cast(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. diff --git a/clang/test/Analysis/explain-svals.cpp b/clang/test/Analysis/explain-svals.cpp --- a/clang/test/Analysis/explain-svals.cpp +++ b/clang/test/Analysis/explain-svals.cpp @@ -54,7 +54,7 @@ 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; }