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,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 Index: clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ 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; 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; @@ -55,17 +57,19 @@ // Optional parameter `ExprVal` for expression value to be marked interesting. ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C, Optional ExprVal = None) const; - ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, - ExplodedNode *N, + ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, 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 +94,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 +139,7 @@ ProgramStateRef StTrue, StFalse; std::tie(StTrue, StFalse) = - State->assume(AssertionVal.castAs()); + State->assume(AssertionVal.castAs()); if (StTrue) { if (StFalse) @@ -155,8 +163,7 @@ } ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - BugReporter &BR, - ExplodedNode *N, + BugReporter &BR, ExplodedNode *N, Optional ExprVal) const { if (!N) return nullptr; @@ -172,6 +179,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 +246,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 +271,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 +282,46 @@ 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; + + QualType ElementTy; + if (const auto *TVR = MR->getAs()) { + ElementTy = TVR->getValueType(); + } else { + ElementTy = + MR->castAs()->getSymbol()->getType()->getPointeeType(); + } + + assert(!ElementTy->isPointerType()); + + DefinedOrUnknownSVal ElementCount = + getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); + + 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 +329,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 +370,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 +437,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 +456,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 +473,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 @@ -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); } Index: clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ 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! Index: clang/lib/StaticAnalyzer/Core/DynamicSize.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/DynamicSize.cpp +++ 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 Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ 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); } 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" @@ -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. Index: clang/test/Analysis/explain-svals.cpp =================================================================== --- clang/test/Analysis/explain-svals.cpp +++ 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; } 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,157 @@ +// RUN: %clang_analyze_cc1 -std=c++20 \ +// 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 *); + +int clang_analyzer_getExtent(void *); +void clang_analyzer_eval(bool); + +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 {{(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 {{(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(12); + 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); +} + +void signedness_equality() { + char *a = new char[sizeof(char) * 13]; + char *b = (char *)malloc(13); + + clang_analyzer_dump(clang_analyzer_getExtent(a)); // expected-warning {{13 S64b}} + clang_analyzer_dump(clang_analyzer_getExtent(b)); // expected-warning {{13 U64b}} + clang_analyzer_eval(clang_analyzer_getExtent(a) == + clang_analyzer_getExtent(b)); + // expected-warning@-2 {{TRUE}} + + delete[] a; + free(b); +} + +void default_new_aligned() { + struct alignas(32) S {}; + + S *a = new S[10]; + + clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning {{320 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{10 S64b}} + + delete[] a; +} + +void *operator new[](std::size_t, std::align_val_t, bool hack) throw(); + +void user_defined_new() { + int *a = new (std::align_val_t(32), true) int[10]; + + clang_analyzer_dump(a); // expected-warning {{Element{SymRegion{conj}} + clang_analyzer_dumpExtent(a); // expected-warning-re {{{{^extent_\$[0-9]\{SymRegion{conj}}}} + clang_analyzer_dumpElementCount(a); // expected-warning-re {{{{^\(extent_\$[0-9]\{SymRegion{conj.*\) / 4}}}} + + operator delete[](a, std::align_val_t(32)); +}