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 @@ -21,8 +21,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 @@ -43,6 +43,9 @@ 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 analyzerRegion(const CallExpr *CE, CheckerContext &C) const; + void analyzerDumpSize(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; @@ -54,13 +57,16 @@ ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N) 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 *) @@ -73,26 +79,36 @@ // These checks should have no effect on the surrounding environment // (globals should not be invalidated, etc), hence the use of evalCall. - FnCheck Handler = llvm::StringSwitch(C.getCalleeName(CE)) - .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) - .Case("clang_analyzer_checkInlined", - &ExprInspectionChecker::analyzerCheckInlined) - .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) - .Case("clang_analyzer_warnIfReached", - &ExprInspectionChecker::analyzerWarnIfReached) - .Case("clang_analyzer_warnOnDeadSymbol", - &ExprInspectionChecker::analyzerWarnOnDeadSymbol) - .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) - .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) - .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) - .Case("clang_analyzer_printState", - &ExprInspectionChecker::analyzerPrintState) - .Case("clang_analyzer_numTimesReached", - &ExprInspectionChecker::analyzerNumTimesReached) - .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) - .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) - .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress) - .Default(nullptr); + FnCheck Handler = + llvm::StringSwitch(C.getCalleeName(CE)) + .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) + .Case("clang_analyzer_checkInlined", + &ExprInspectionChecker::analyzerCheckInlined) + .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) + .Case("clang_analyzer_warnIfReached", + &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnOnDeadSymbol", + &ExprInspectionChecker::analyzerWarnOnDeadSymbol) + .StartsWith("clang_analyzer_explain", + &ExprInspectionChecker::analyzerExplain) + .StartsWith("clang_analyzer_dump", + &ExprInspectionChecker::analyzerDump) + .Case("clang_analyzer_getExtent", + &ExprInspectionChecker::analyzerGetExtent) + .Case("clang_analyzer_region", &ExprInspectionChecker::analyzerRegion) + .Case("clang_analyzer_size", &ExprInspectionChecker::analyzerDumpSize) + .Case("clang_analyzer_elementCount", + &ExprInspectionChecker::analyzerDumpElementCount) + .Case("clang_analyzer_printState", + &ExprInspectionChecker::analyzerPrintState) + .Case("clang_analyzer_numTimesReached", + &ExprInspectionChecker::analyzerNumTimesReached) + .Case("clang_analyzer_hashDump", + &ExprInspectionChecker::analyzerHashDump) + .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) + .Case("clang_analyzer_express", + &ExprInspectionChecker::analyzerExpress) + .Default(nullptr); if (!Handler) return false; @@ -118,7 +134,7 @@ ProgramStateRef StTrue, StFalse; std::tie(StTrue, StFalse) = - State->assume(AssertionVal.castAs()); + State->assume(AssertionVal.castAs()); if (StTrue) { if (StFalse) @@ -153,6 +169,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 = nullptr; + if (!(Arg = getArgExpr(CE, C))) + 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(); @@ -196,24 +236,22 @@ void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing argument for explaining", C); + const Expr *Arg = nullptr; + if (!(Arg = getArgExpr(CE, C))) 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 = nullptr; + if (!(Arg = getArgExpr(CE, C))) return; - } - SVal V = C.getSVal(CE->getArg(0)); + SVal V = C.getSVal(Arg); llvm::SmallString<32> Str; llvm::raw_svector_ostream OS(Str); @@ -223,16 +261,9 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("Missing region for obtaining extent", C); + const MemRegion *MR = nullptr; + if (!(MR = getArgRegion(CE, C))) 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()); @@ -241,6 +272,53 @@ C.addTransition(State); } +void ExprInspectionChecker::analyzerRegion(const CallExpr *CE, + CheckerContext &C) const { + const MemRegion *MR = nullptr; + if (!(MR = getArgRegion(CE, C))) + return; + + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << MR; + reportBug(Out.str(), C); +} + +void ExprInspectionChecker::analyzerDumpSize(const CallExpr *CE, + CheckerContext &C) const { + const MemRegion *MR = nullptr; + if (!(MR = getArgRegion(CE, C))) + 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 = nullptr; + if (!(MR = getArgRegion(CE, C))) + return; + + const auto *TVR = MR->getAs(); + + QualType Ty = TVR ? TVR->getDesugaredValueType(C.getASTContext()) + : CE->getArg(0)->IgnoreParenImpCasts()->getType(); + + 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(); @@ -248,9 +326,11 @@ void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) + const Expr *Arg = nullptr; + if (!(Arg = getArgExpr(CE, C))) return; - SVal Val = C.getSVal(CE->getArg(0)); + + SVal Val = C.getSVal(Arg); SymbolRef Sym = Val.getAsSymbol(); if (!Sym) return; @@ -287,7 +367,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; @@ -354,9 +434,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)) @@ -375,7 +453,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; } @@ -391,12 +470,11 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) { - reportBug("clang_analyzer_express() requires a symbol", C); + const Expr *Arg = nullptr; + if (!(Arg = getArgExpr(CE, C))) return; - } - SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); + SymbolRef Sym = C.getSVal(Arg).getAsSymbol(); if (!Sym) { reportBug("Not a symbol", C); return; Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -466,9 +466,6 @@ ProgramStateRef State, AllocationFamily Family = AF_Malloc); - 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 @@ -1354,7 +1351,8 @@ // existing binding. State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray : AF_CXXNew, 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); } @@ -1371,52 +1369,6 @@ processNewAllocation(NE, C, Target); } -// 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 { @@ -1537,21 +1489,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 @@ -166,18 +166,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,88 @@ #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; + + MR = getSuperRegion(MR); + + // Convert to signed int. + if (const auto CI = Size.getAs()) + if (CI->getValue().isUnsigned()) + Size = SVB.makeIntVal(CI->getValue(), /*IsUnsigned=*/false); + + 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=*/false); + } + + 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; @@ -761,11 +762,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,108 @@ +// 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_region(int); +void clang_analyzer_region(const void *); +void clang_analyzer_size(int); +void clang_analyzer_size(const void *); +void clang_analyzer_elementCount(int); +void clang_analyzer_elementCount(const void *); + +void var_region_simple_ref() { + int a = 13; + clang_analyzer_region(&a); // expected-warning {{a}} + clang_analyzer_size(&a); // expected-warning {{4 S64b}} + clang_analyzer_elementCount(&a); // expected-warning {{1 S64b}} +} + +void var_region_simple_ptr(int *a) { + clang_analyzer_region(a); // expected-warning {{SymRegion{reg_$0}}} + clang_analyzer_size(a); // expected-warning {{extent_$1{SymRegion{reg_$0}}}} + clang_analyzer_elementCount(a); // expected-warning {{(extent_$1{SymRegion{reg_$0}}) / 8}} +} + +void var_region_array() { + int a[] = {1, 2, 3}; + clang_analyzer_region(a); // expected-warning {{Element{a,0 S64b,int}}} + clang_analyzer_size(a); // expected-warning {{12 S64b}} + clang_analyzer_elementCount(a); // expected-warning {{3 S64b}} +} + +void string_region() { + clang_analyzer_region("foo"); // expected-warning {{Element{"foo",0 S64b,char}}} + clang_analyzer_size("foo"); // expected-warning {{4 S64b}} + clang_analyzer_elementCount("foo"); // expected-warning {{4 S64b}} +} + +void field_region_ref(S a) { + clang_analyzer_region(&a.f); // expected-warning {{a.f}} + clang_analyzer_size(&a.f); // expected-warning {{4 S64b}} + clang_analyzer_elementCount(&a.f); // expected-warning {{1 S64b}} +} + +void field_region_ptr(S *a) { + clang_analyzer_region(&a->f); // expected-warning {{SymRegion{reg_$0}.f}} + clang_analyzer_size(&a->f); // expected-warning {{4 S64b}} + clang_analyzer_elementCount(&a->f); // expected-warning {{1 S64b}} +} + +void symbolic_array() { + int *a = new int[3]; + clang_analyzer_region(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_size(a); // expected-warning {{12 S64b}} + clang_analyzer_elementCount(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_region(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_size(a); // expected-warning {{12 S64b}} + clang_analyzer_elementCount(a); // expected-warning {{3 S64b}} + delete[] buf; +} + +void symbolic_malloc() { + int *a = (int *)malloc(12); + clang_analyzer_region(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_size(a); // expected-warning {{12 S64b}} + clang_analyzer_elementCount(a); // expected-warning {{3 S64b}} + free(a); +} + +void symbolic_alloca() { + int *a = (int *)alloca(12); + clang_analyzer_region(a); // expected-warning {{Element{HeapSymRegion{conj}} + clang_analyzer_size(a); // expected-warning {{12 S64b}} + clang_analyzer_elementCount(a); // expected-warning {{3 S64b}} +} + +void symbolic_complex() { + int *a = (int *)malloc(4); + clang_analyzer_size(a); // expected-warning {{4 S64b}} + clang_analyzer_elementCount(a); // expected-warning {{1 S64b}} + + int *b = (int *)realloc(a, sizeof(int) * 2); + clang_analyzer_size(b); // expected-warning {{8 S64b}} + clang_analyzer_elementCount(b); // expected-warning {{2 S64b}} + free(b); + + int *c = (int *)calloc(3, 4); + clang_analyzer_size(c); // expected-warning {{12 S64b}} + clang_analyzer_elementCount(c); // expected-warning {{3 S64b}} + free(c); +} Index: clang/test/Analysis/misc-ps-region-store.m =================================================================== --- clang/test/Analysis/misc-ps-region-store.m +++ clang/test/Analysis/misc-ps-region-store.m @@ -1186,7 +1186,8 @@ for (y = 0; y < b_h; y++) { for (x = 0; x < b_w + 1; x++) { int am = 0; - tmp2[x] = am; + tmp2[x] = am; // expected-warning \ + {{Access out-of-bound array element (buffer overflow)}} } } }