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,16 +22,24 @@ 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 stored dynamic size expression for the region \p MR. +const Expr *getDynamicSizeExpr(ProgramStateRef State, const MemRegion *MR); + +/// \returns The stored element count of the region \p MR. DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, QualType ElementTy); +/// Set the dynamic size \p Size with its expression \p SizeExpr of the region +/// \p MR. +ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR, + DefinedOrUnknownSVal Size, const Expr *SizeExpr); + } // namespace ento } // namespace clang Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSizeInfo.h =================================================================== --- /dev/null +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSizeInfo.h @@ -0,0 +1,45 @@ +//===- DynamicSizeInfo.h - Runtime size information -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICSIZEINFO_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICSIZEINFO_H + +namespace clang { +namespace ento { + +#include "clang/AST/Expr.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" + +/// Helper class to store information about the dynamic size. +class DynamicSizeInfo { +public: + DynamicSizeInfo(DefinedOrUnknownSVal Size, const Expr *SizeExpr = nullptr) + : Size(Size), SizeExpr(SizeExpr) {} + + DefinedOrUnknownSVal getSize() const { return Size; } + const Expr *getSizeExpr() const { return SizeExpr; } + + bool operator==(const DynamicSizeInfo &RHS) const { + return Size == RHS.Size && SizeExpr == RHS.SizeExpr; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(Size); + ID.AddPointer(SizeExpr); + } + +private: + DefinedOrUnknownSVal Size; + const Expr *SizeExpr; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICSIZEINFO_H 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(), + Call.getArgExpr(0)); C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); return true; Index: clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -1067,12 +1067,10 @@ // For now we can only handle the case of offset is 0 and concrete char value. if (Offset.isValid() && !Offset.hasSymbolicOffset() && Offset.getOffset() == 0) { - // Get the base region's size. - DefinedOrUnknownSVal SizeDV = getDynamicSize(State, BR, svalBuilder); - ProgramStateRef StateWholeReg, StateNotWholeReg; - std::tie(StateWholeReg, StateNotWholeReg) = - State->assume(svalBuilder.evalEQ(State, SizeDV, *SizeNL)); + std::tie(StateWholeReg, StateNotWholeReg) = State->assume(*SizeNL); + + setDynamicSize(State, BR, *SizeNL, Size); // With the semantic of 'memset()', we should convert the CharVal to // unsigned char. Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -441,29 +441,30 @@ /// Models memory allocation. /// /// \param [in] CE The expression that allocates memory. - /// \param [in] SizeEx Size of the memory that needs to be allocated. + /// \param [in] SizeExpr Size of the memory that needs to be allocated. /// \param [in] Init The value the allocated memory needs to be initialized. /// with. For example, \c calloc initializes the allocated memory to 0, /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, - const Expr *SizeEx, SVal Init, + const Expr *SizeExpr, SVal Init, ProgramStateRef State, AllocationFamily Family = AF_Malloc); /// Models memory allocation. /// /// \param [in] CE The expression that allocates memory. - /// \param [in] Size Size of the memory that needs to be allocated. + /// \param [in] Size Symbolic size of the memory that needs to be allocated. + /// \param [in] SizeExpr Size expression of the memory allocation. /// \param [in] Init The value the allocated memory needs to be initialized. /// with. For example, \c calloc initializes the allocated memory to 0, /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, - SVal Size, SVal Init, - ProgramStateRef State, + const Expr *SizeExpr, SVal Size, + SVal Init, ProgramStateRef State, AllocationFamily Family = AF_Malloc); static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE, @@ -1163,8 +1164,8 @@ } else if (FunI == MemFunctionInfo.II_if_nameindex) { // Should we model this differently? We can allocate a fixed number of // elements with zeros in the last one. - State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, - AF_IfNameIndex); + State = MallocMemAux(C, CE, /*SizeExpr=*/nullptr, UnknownVal(), + UnknownVal(), State, AF_IfNameIndex); } else if (FunI == MemFunctionInfo.II_if_freenameindex) { State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); } else if (FunI == MemFunctionInfo.II_g_malloc0 || @@ -1193,7 +1194,8 @@ Init = SB.makeZeroVal(SB.getContext().CharTy); } SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); - State = MallocMemAux(C, CE, TotalSize, Init, State); + State = MallocMemAux(C, CE, /*SizeExpr=*/CE->getArg(0), TotalSize, Init, + State); State = ProcessZeroAllocCheck(C, CE, 0, State); State = ProcessZeroAllocCheck(C, CE, 1, State); } else if (FunI == MemFunctionInfo.II_g_realloc_n || @@ -1403,16 +1405,15 @@ 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); + + State = setDynamicSize(State, Region, + SizeInBytes.castAs(), + NE->getArraySize().getValueOr(nullptr)); } return State; } @@ -1495,25 +1496,27 @@ return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(), State); } - return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State); + return MallocMemAux(C, CE, /*SizeExpr=*/nullptr, UnknownVal(), UndefinedVal(), + State); } ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, - const Expr *SizeEx, SVal Init, + const Expr *SizeExpr, SVal Init, ProgramStateRef State, AllocationFamily Family) { if (!State) return nullptr; - return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family); + return MallocMemAux(C, CE, SizeExpr, C.getSVal(SizeExpr), Init, State, + Family); } ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, - const CallExpr *CE, - SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family) { + const CallExpr *CE, + const Expr *SizeExpr, SVal Size, + SVal Init, ProgramStateRef State, + AllocationFamily Family) { if (!State) return nullptr; @@ -1534,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 size. + State = setDynamicSize(State, RetVal.getAsRegion(), + Size.castAs(), SizeExpr); return MallocUpdateRefState(C, CE, State, Family); } @@ -2442,8 +2433,8 @@ // If the ptr is NULL and the size is not 0, the call is equivalent to // malloc(size). if (PrtIsNull && !SizeIsZero) { - ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize, - UndefinedVal(), StatePtrIsNull); + ProgramStateRef stateMalloc = + MallocMemAux(C, CE, Arg1, TotalSize, UndefinedVal(), StatePtrIsNull); return stateMalloc; } @@ -2474,8 +2465,8 @@ if (ProgramStateRef stateFree = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) { - ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize, - UnknownVal(), stateFree); + ProgramStateRef stateRealloc = + MallocMemAux(C, CE, Arg1, TotalSize, UnknownVal(), stateFree); if (!stateRealloc) return nullptr; @@ -2508,7 +2499,7 @@ SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); - return MallocMemAux(C, CE, TotalSize, zeroVal, State); + return MallocMemAux(C, CE, CE->getArg(0), TotalSize, zeroVal, State); } MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N, Index: clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -166,18 +166,9 @@ 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(), SE); // 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 @@ -12,20 +12,40 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" #include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSizeInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #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::DynamicSizeInfo) + namespace clang { namespace ento { +ProgramStateRef setDynamicSize(ProgramStateRef State, const MemRegion *MR, + DefinedOrUnknownSVal Size, + const Expr *SizeExpr) { + return State->set(MR, {Size, SizeExpr}); +} + DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB) { + if (const DynamicSizeInfo *SizeInfo = State->get(MR)) + return SizeInfo->getSize(); + return MR->getMemRegionManager().getStaticSize(MR, SVB); } +const Expr *getDynamicSizeExpr(ProgramStateRef State, const MemRegion *MR) { + if (const DynamicSizeInfo *SizeInfo = State->get(MR)) + return SizeInfo->getSizeExpr(); + + return nullptr; +} + DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, 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,20 @@ // heap. We realize this is an approximation that might not correctly model // a custom global allocator. if (symVal.isUnknown()) { - if (IsStandardGlobalOpNewFunction) + if (IsStandardGlobalOpNewFunction) { + DefinedOrUnknownSVal Size = UnknownVal(); + const Expr *SizeExpr = nullptr; + + if ((SizeExpr = CNE->getArraySize().getValueOr(nullptr))) { + Size = State->getSVal(SizeExpr, LCtx).castAs(); + } + symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); - else + State = setDynamicSize(State, symVal.getAsRegion(), Size, SizeExpr); + } 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,7 +10,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "PrettyStackTraceLocationContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" @@ -18,6 +17,8 @@ #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" @@ -653,16 +654,21 @@ // 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()) { + // FIXME: Delegate this to evalCall in MallocChecker? + DefinedOrUnknownSVal Size = UnknownVal(); + const Expr *SizeExpr = nullptr; + + if ((SizeExpr = CNE->getArraySize().getValueOr(nullptr))) { + Size = State->getSVal(SizeExpr, LCtx).castAs(); } - R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count) - : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, - Count); + R = svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count); + State = setDynamicSize(State, R.getAsRegion(), Size, SizeExpr); + } else { + R = svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); + } } return State->BindExpr(E, LCtx, R); } Index: clang/test/Analysis/null-deref-ps-region.c =================================================================== --- clang/test/Analysis/null-deref-ps-region.c +++ clang/test/Analysis/null-deref-ps-region.c @@ -33,7 +33,7 @@ void bar() { int *x = malloc(sizeof(int)); memset(x, 0, 1); - int n = 1 / *x; // no-warning + int n = 1 / *x; // expected-warning {{Division by zero}} free(x); } Index: clang/test/Analysis/string.c =================================================================== --- clang/test/Analysis/string.c +++ clang/test/Analysis/string.c @@ -1274,7 +1274,7 @@ void memset5_char_malloc_overflow_null() { char *str = (char *)malloc(10 * sizeof(char)); memset(str, '\0', 12); - clang_analyzer_eval(str[1] == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(str[1] == 0); // expected-warning{{TRUE}} free(str); } #endif @@ -1362,7 +1362,7 @@ char *str = (char *)malloc(10 * sizeof(int)); int *array = (int *)str; memset(array, 0, 5 * sizeof(int)); - clang_analyzer_eval(str[10] == '\0'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(str[10] == '\0'); // expected-warning{{TRUE}} clang_analyzer_eval(strlen((char *)array) == 0); // expected-warning{{TRUE}} clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} free(str); @@ -1380,7 +1380,7 @@ int memset21_scalar() { int *x = malloc(sizeof(int)); memset(x, 0, 1); - int num = 1 / *x; + int num = 1 / *x; // expected-warning{{Division by zero}} free(x); return num; } @@ -1476,7 +1476,7 @@ #endif clang_analyzer_eval(privkey[0] == '\0'); #ifdef SUPPRESS_OUT_OF_BOUND - // expected-warning@-2 {{UNKNOWN}} + // expected-warning@-2 {{TRUE}} #endif free(privkey); }