Index: clang/docs/analyzer/developer-docs/DebugChecks.rst =================================================================== --- clang/docs/analyzer/developer-docs/DebugChecks.rst +++ clang/docs/analyzer/developer-docs/DebugChecks.rst @@ -297,6 +297,19 @@ return n; } +- ``clang_analyzer_dumpExtent(a single argument of any type)`` +- ``clang_analyzer_dumpElementCount(a single argument of any type)`` + + Dumps out the extent and the element count of the argument. + + Example usage:: + + void array() { + int a[] = {1, 3}; + clang_analyzer_dumpExtent(a); // expected-warning {{8 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{2 S64b}} + } + Statistics ========== Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h @@ -22,15 +22,26 @@ 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); + +/// Set the dynamic size of a CXXNewExpr \p NE by its region \p MR. +ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR, + const CXXNewExpr *NE, + const LocationContext *LCtx, SValBuilder &SVB); } // namespace ento } // namespace clang Index: clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -90,12 +90,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; Index: clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -22,8 +22,8 @@ using namespace ento; namespace { -class ExprInspectionChecker : public Checker { +class ExprInspectionChecker + : public Checker { mutable std::unique_ptr BT; // These stats are per-analysis, not per-branch, hence they shouldn't @@ -44,6 +44,8 @@ void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; + void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const; + void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const; void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const; void analyzerDenote(const CallExpr *CE, CheckerContext &C) const; void analyzerExpress(const CallExpr *CE, CheckerContext &C) const; @@ -59,13 +61,16 @@ ExplodedNode *N, Optional ExprVal = None) const; + const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; + const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const; + public: bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const; }; -} +} // namespace REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *) @@ -90,6 +95,10 @@ &ExprInspectionChecker::analyzerWarnOnDeadSymbol) .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) + .Case("clang_analyzer_dumpExtent", + &ExprInspectionChecker::analyzerDumpExtent) + .Case("clang_analyzer_dumpElementCount", + &ExprInspectionChecker::analyzerDumpElementCount) .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) .Case("clang_analyzer_getExtent", @@ -131,7 +140,7 @@ ProgramStateRef StTrue, StFalse; std::tie(StTrue, StFalse) = - State->assume(AssertionVal.castAs()); + State->assume(AssertionVal.castAs()); if (StTrue) { if (StFalse) @@ -172,6 +181,30 @@ return N; } +const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("Missing argument", C); + return nullptr; + } + return CE->getArg(0); +} + +const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE, + CheckerContext &C) const { + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) + return nullptr; + + const MemRegion *MR = C.getSVal(Arg).getAsRegion(); + if (!MR) { + reportBug("Cannot obtain the region", C); + return nullptr; + } + + return MR; +} + void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { const LocationContext *LC = C.getPredecessor()->getLocationContext(); @@ -215,24 +248,22 @@ void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing argument for explaining", C); + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - } - SVal V = C.getSVal(CE->getArg(0)); + SVal V = C.getSVal(Arg); SValExplainer Ex(C.getASTContext()); reportBug(Ex.Visit(V), C); } void ExprInspectionChecker::analyzerDump(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing argument for dumping", C); + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - } - SVal V = C.getSVal(CE->getArg(0)); + SVal V = C.getSVal(Arg); llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -242,16 +273,9 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing region for obtaining extent", C); + const MemRegion *MR = getArgRegion(CE, C); + if (!MR) return; - } - - auto MR = dyn_cast_or_null(C.getSVal(CE->getArg(0)).getAsRegion()); - if (!MR) { - reportBug("Obtaining extent of a non-region", C); - return; - } ProgramStateRef State = C.getState(); DefinedOrUnknownSVal Size = getDynamicSize(State, MR, C.getSValBuilder()); @@ -260,6 +284,43 @@ C.addTransition(State); } +void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, + CheckerContext &C) const { + const MemRegion *MR = getArgRegion(CE, C); + if (!MR) + return; + + DefinedOrUnknownSVal Size = + getDynamicSize(C.getState(), MR, C.getSValBuilder()); + + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << Size; + reportBug(Out.str(), C); +} + +void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, + CheckerContext &C) const { + const MemRegion *MR = getArgRegion(CE, C); + if (!MR) + return; + + const auto *TVR = MR->getAs(); + + QualType Ty = TVR ? TVR->getValueType() + : CE->getArg(0)->IgnoreParenImpCasts()->getType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + + DefinedOrUnknownSVal ElementCount = + getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), Ty); + + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << ElementCount; + reportBug(Out.str(), C); +} + void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, CheckerContext &C) const { C.getState()->dump(); @@ -267,9 +328,11 @@ void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - SVal Val = C.getSVal(CE->getArg(0)); + + SVal Val = C.getSVal(Arg); SymbolRef Sym = Val.getAsSymbol(); if (!Sym) return; @@ -306,7 +369,7 @@ void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { - for (auto Item: ReachedStats) { + for (auto Item : ReachedStats) { unsigned NumTimesReached = Item.second.NumTimesReached; ExplodedNode *N = Item.second.ExampleNode; @@ -373,9 +436,7 @@ return None; } - Optional VisitSymExpr(const SymExpr *S) { - return lookup(S); - } + Optional VisitSymExpr(const SymExpr *S) { return lookup(S); } Optional VisitSymIntExpr(const SymIntExpr *S) { if (Optional Str = lookup(S)) @@ -394,7 +455,8 @@ if (Optional Str1 = Visit(S->getLHS())) if (Optional Str2 = Visit(S->getRHS())) return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + - " " + *Str2).str(); + " " + *Str2) + .str(); return None; } @@ -410,10 +472,9 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("clang_analyzer_express() requires a symbol", C); + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - } SVal ArgVal = C.getSVal(CE->getArg(0)); SymbolRef Sym = ArgVal.getAsSymbol(); Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -503,9 +503,6 @@ ProgramStateRef State, AllocationFamily Family); - 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::Optional @@ -1344,7 +1341,8 @@ // MallocUpdateRefState() instead of MallocMemAux() which breaks the // existing binding. State = MallocUpdateRefState(C, NE, State, Family, Target); - State = addExtentSize(C, NE, State, Target); + State = setDynamicSize(State, Target.getAsRegion(), NE, + C.getLocationContext(), C.getSValBuilder()); State = ProcessZeroAllocCheck(C, NE, 0, State, Target); C.addTransition(State); } @@ -1366,52 +1364,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; -} - void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const { @@ -1531,21 +1483,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 size. + State = setDynamicSize(State, RetVal.getAsRegion(), + Size.castAs(), svalBuilder); return MallocUpdateRefState(C, CE, State, Family); } Index: clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -94,6 +94,9 @@ return; // FIXME: Handle multi-dimensional VLAs. + if (VLA->getElementType()->getAsArrayTypeUnsafe()) + return; + const Expr *SE = VLA->getSizeExpr(); ProgramStateRef state = C.getState(); SVal sizeV = C.getSVal(SE); @@ -166,18 +169,10 @@ SVal ArraySizeVal = svalBuilder.evalBinOpNN( state, BO_Mul, ArrayLength, EleSizeVal.castAs(), SizeTy); - // Finally, assume that the array's size matches the given size. - const LocationContext *LC = C.getLocationContext(); - DefinedOrUnknownSVal DynSize = - getDynamicSize(state, state->getRegion(VD, LC), svalBuilder); - - DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs(); - DefinedOrUnknownSVal sizeIsKnown = - svalBuilder.evalEQ(state, DynSize, ArraySize); - state = state->assume(sizeIsKnown, true); - - // Assume should not fail at this point. - assert(state); + // Finally, set the size. + state = + setDynamicSize(state, state->getRegion(VD, C.getLocationContext()), + ArraySizeVal.castAs(), svalBuilder); // Remember our assumptions! C.addTransition(state); Index: clang/lib/StaticAnalyzer/Core/DynamicSize.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/DynamicSize.cpp +++ clang/lib/StaticAnalyzer/Core/DynamicSize.cpp @@ -19,30 +19,86 @@ #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 { +/// Helper to bypass the top-level ElementRegion of \p MR. +static const MemRegion *getSuperRegion(const MemRegion *MR) { + assert(MR); + if (const auto *ER = MR->getAs()) + MR = ER->getSuperRegion(); + return MR; +} + DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB) { + MR = getSuperRegion(MR); + + 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()); +} + +static DefinedOrUnknownSVal getSize(ProgramStateRef State, SVal ElementCount, + QualType Ty, SValBuilder &SVB) { + DefinedOrUnknownSVal ElementSize = getElementSize(Ty, SVB); + + return SVB + .evalBinOp(State, BO_Mul, ElementCount, ElementSize, + SVB.getArrayIndexType()) + .castAs(); +} + DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, - SValBuilder &SVB, - QualType ElementTy) { - MemRegionManager &MemMgr = MR->getMemRegionManager(); - ASTContext &Ctx = MemMgr.getContext(); + SValBuilder &SVB, QualType Ty) { + MR = getSuperRegion(MR); DefinedOrUnknownSVal Size = getDynamicSize(State, MR, SVB); - SVal ElementSizeV = SVB.makeIntVal( - Ctx.getTypeSizeInChars(ElementTy).getQuantity(), SVB.getArrayIndexType()); + SVal ElementSize = getElementSize(Ty, SVB); SVal DivisionV = - SVB.evalBinOp(State, BO_Div, Size, ElementSizeV, SVB.getArrayIndexType()); + SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType()); return DivisionV.castAs(); } +ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR, + DefinedOrUnknownSVal Size, SValBuilder &SVB) { + if (Size.isUnknown()) + return State; + + /* FIXME: Make this work. + if (const auto CI = Size.getAs()) + assert(CI->getValue().isUnsigned());*/ + + MR = getSuperRegion(MR); + return State->set(MR, Size); +} + +ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR, + const CXXNewExpr *NE, + const LocationContext *LCtx, SValBuilder &SVB) { + SVal ElementCount; + if (const Expr *SizeExpr = NE->getArraySize().getValueOr(nullptr)) { + ElementCount = State->getSVal(SizeExpr, LCtx); + } else { + ElementCount = SVB.makeIntVal(1, /*IsUnsigned=*/true); + } + + return setDynamicSize( + State, MR, getSize(State, ElementCount, NE->getAllocatedType(), SVB), + SVB); +} + } // namespace ento } // namespace clang Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -10,15 +10,16 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/Analysis/ConstructionContext.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/StmtCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; @@ -790,11 +791,14 @@ // heap. We realize this is an approximation that might not correctly model // a custom global allocator. if (symVal.isUnknown()) { - if (IsStandardGlobalOpNewFunction) + if (IsStandardGlobalOpNewFunction) { symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); - else + State = + setDynamicSize(State, symVal.getAsRegion(), CNE, LCtx, svalBuilder); + } else { symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(), blockCount); + } } CallEventManager &CEMgr = getStateManager().getCallEventManager(); Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -10,15 +10,16 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/Decl.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "PrettyStackTraceLocationContext.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/Analysis/Analyses/LiveVariables.h" #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" #include "llvm/Support/SaveAndRestore.h" @@ -690,16 +691,15 @@ // 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); - R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count) - : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, - Count); + // FIXME: Delegate this to evalCall in MallocChecker? + State = setDynamicSize(State, R.getAsRegion(), CNE, LCtx, svalBuilder); + } else { + R = svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); + } } return State->BindExpr(E, LCtx, R); } Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ 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" @@ -667,13 +668,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); @@ -703,7 +697,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. @@ -711,7 +705,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. Index: clang/test/Analysis/explain-svals.cpp =================================================================== --- clang/test/Analysis/explain-svals.cpp +++ clang/test/Analysis/explain-svals.cpp @@ -49,7 +49,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; } Index: clang/test/Analysis/expr-inspection.cpp =================================================================== --- clang/test/Analysis/expr-inspection.cpp +++ clang/test/Analysis/expr-inspection.cpp @@ -12,7 +12,7 @@ void foo(int x, unsigned y) { clang_analyzer_denote(); // expected-warning{{clang_analyzer_denote() requires a symbol and a string literal}} - clang_analyzer_express(); // expected-warning{{clang_analyzer_express() requires a symbol}} + clang_analyzer_express(); // expected-warning{{Missing argument}} clang_analyzer_denote(x); // expected-warning{{clang_analyzer_denote() requires a symbol and a string literal}} clang_analyzer_express(x); // expected-warning{{Unable to express}} Index: clang/test/Analysis/memory-model.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/memory-model.cpp @@ -0,0 +1,114 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,unix,cplusplus,debug.ExprInspection \ +// RUN: -triple x86_64-unknown-linux-gnu \ +// RUN: -verify %s + +#include "Inputs/system-header-simulator-cxx.h" + +typedef __SIZE_TYPE__ size_t; +void *malloc(size_t); +void *alloca(size_t); +void *realloc(void *ptr, size_t size); +void *calloc(size_t number, size_t size); +void free(void *); + +struct S { int f; }; + +void clang_analyzer_dump(int); +void clang_analyzer_dump(const void *); +void clang_analyzer_dumpExtent(int); +void clang_analyzer_dumpExtent(const void *); +void clang_analyzer_dumpElementCount(int); +void clang_analyzer_dumpElementCount(const void *); + +void var_simple_ref() { + int a = 13; + clang_analyzer_dump(&a); // expected-warning {{a}} + clang_analyzer_dumpExtent(&a); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount(&a); // expected-warning {{1 S64b}} +} + +void var_simple_ptr(int *a) { + clang_analyzer_dump(a); // expected-warning {{SymRegion{reg_$0}}} + clang_analyzer_dumpExtent(a); // expected-warning {{extent_$1{SymRegion{reg_$0}}}} + clang_analyzer_dumpElementCount(a); // expected-warning-re {{{{^\(extent_\$1\{SymRegion\{reg_\$0\}\}\) / 4$}}}} +} + +void var_array() { + int a[] = {1, 2, 3}; + clang_analyzer_dump(a); // expected-warning {{Element{a,0 S64b,int}}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} +} + +void string() { + clang_analyzer_dump("foo"); // expected-warning {{Element{"foo",0 S64b,char}}} + clang_analyzer_dumpExtent("foo"); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount("foo"); // expected-warning {{4 S64b}} +} + +void struct_simple_ptr(S *a) { + clang_analyzer_dump(a); // expected-warning {{SymRegion{reg_$0}}} + clang_analyzer_dumpExtent(a); // expected-warning {{extent_$1{SymRegion{reg_$0}}}} + clang_analyzer_dumpElementCount(a); // expected-warning-re {{{{^\(extent_\$1\{SymRegion\{reg_\$0\}\}\) / 4$}}}} +} + +void field_ref(S a) { + clang_analyzer_dump(&a.f); // expected-warning {{a.f}} + clang_analyzer_dumpExtent(&a.f); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount(&a.f); // expected-warning {{1 S64b}} +} + +void field_ptr(S *a) { + clang_analyzer_dump(&a->f); // expected-warning {{SymRegion{reg_$0}.f}} + clang_analyzer_dumpExtent(&a->f); // expected-warning {{4 S64b}} + clang_analyzer_dumpElementCount(&a->f); // expected-warning {{1 S64b}} +} + +void symbolic_array() { + int *a = new int[3]; + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} + delete[] a; +} + +void symbolic_placement_new() { + char *buf = new char[sizeof(int) * 3]; + int *a = new (buf) int(13); + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} + delete[] buf; +} + +void symbolic_malloc() { + int *a = (int *)malloc(12); + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 U64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} + free(a); +} + +void symbolic_alloca() { + int *a = (int *)alloca(12); + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 U64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} +} + +void symbolic_complex() { + int *a = (int *)malloc(4); + clang_analyzer_dumpExtent(a); // expected-warning {{4 U64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{1 S64b}} + + int *b = (int *)realloc(a, sizeof(int) * 2); + clang_analyzer_dumpExtent(b); // expected-warning {{8 U64b}} + clang_analyzer_dumpElementCount(b); // expected-warning {{2 S64b}} + free(b); + + int *c = (int *)calloc(3, 4); + clang_analyzer_dumpExtent(c); // expected-warning {{12 U64b}} + clang_analyzer_dumpElementCount(c); // expected-warning {{3 S64b}} + free(c); +}