ASDenysPetrov updated this revision to Diff 381240. ASDenysPetrov added a comment.
Added a comment to //initialization.c//, that we can reason about values of constant array which doesn't have an initializer. This is not such simple for C++, since there are much more ways to initialize the array. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D106681/new/ https://reviews.llvm.org/D106681 Files: clang/lib/StaticAnalyzer/Core/RegionStore.cpp clang/test/Analysis/initialization.c clang/test/Analysis/initialization.cpp
Index: clang/test/Analysis/initialization.cpp =================================================================== --- clang/test/Analysis/initialization.cpp +++ clang/test/Analysis/initialization.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-config eagerly-assume=false -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection,core.uninitialized.UndefReturn -verify %s +template <typename T> +void clang_analyzer_dump(T x); void clang_analyzer_eval(int); struct S { @@ -32,6 +34,10 @@ auto x = ptr[idx]; // expected-warning{{garbage or undefined}} } +void glob_symbolic_index1(int idx) { + clang_analyzer_dump(glob_arr1[idx]); // expected-warning{{Unknown}} +} + int const glob_arr2[4] = {1, 2}; void glob_ptr_index1() { int const *ptr = glob_arr2; @@ -128,3 +134,15 @@ // FIXME: Should warn {{garbage or undefined}}. auto x = ptr[idx]; // // no-warning } + +extern const int glob_arr_no_init[10]; +void glob_array_index4() { + clang_analyzer_eval(glob_arr_no_init[2]); // expected-warning{{UNKNOWN}} +} + +struct S2 { + static const int arr_no_init[10]; +}; +void struct_arr_index1() { + clang_analyzer_eval(S2::arr_no_init[2]); // expected-warning{{UNKNOWN}} +} Index: clang/test/Analysis/initialization.c =================================================================== --- clang/test/Analysis/initialization.c +++ clang/test/Analysis/initialization.c @@ -97,3 +97,9 @@ // FIXME: Should warn {{garbage or undefined}}. int res = glob_arr2[x][y]; // no-warning } + +const int glob_arr_no_init[10]; +void glob_arr_index4() { + // FIXME: Should warn {{FALSE}}, since the array has a static storage. + clang_analyzer_eval(glob_arr_no_init[2]); // expected-warning{{UNKNOWN}} +} Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -437,6 +437,10 @@ RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, const SubRegion *R); + Optional<SVal> getConstantValFromConstArrayInitializer( + RegionBindingsConstRef B, const VarRegion *VR, const ElementRegion *R); + Optional<SVal> getSValFromInitListExpr(const InitListExpr *ILE, + uint64_t Offset, QualType ElemT); public: // Part of public interface to class. @@ -1625,6 +1629,95 @@ return Result; } +Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer( + RegionBindingsConstRef B, const VarRegion *VR, const ElementRegion *R) { + assert(R && VR && "Regions should not be null"); + + // Check if the containing array has an initialized value that we can trust. + // We can trust a const value or a value of a global initializer in main(). + const VarDecl *VD = VR->getDecl(); + if (!VD->getType().isConstQualified() && + !R->getElementType().isConstQualified() && + (!B.isMainAnalysis() || !VD->hasGlobalStorage())) + return None; + + // Array's declaration should have an initializer. + const Expr *Init = VD->getAnyInitializer(); + if (!Init) + return None; + + // Array's declaration should have ConstantArrayType type, because only this + // type contains an array extent. + const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType()); + if (!CAT) + return None; + + // Array should be one-dimensional. + // TODO: Support multidimensional array. + if (isa<ConstantArrayType>(CAT->getElementType())) // is multidimensional + return None; + + // Array's offset should be a concrete value. + // Return Unknown value if symbolic index presented. + // FIXME: We also need to take ElementRegions with symbolic + // indexes into account. + const auto OffsetVal = R->getIndex().getAs<nonloc::ConcreteInt>(); + if (!OffsetVal.hasValue()) + return UnknownVal(); + + // Check offset for being out of bounds. + // C++20 [expr.add] 7.6.6.4 (excerpt): + // If P points to an array element i of an array object x with n + // elements, where i < 0 or i > n, the behavior is undefined. + // Dereferencing is not allowed on the "one past the last + // element", when i == n. + // Example: + // const int arr[4] = {1, 2}; + // const int *ptr = arr; + // int x0 = ptr[0]; // 1 + // int x1 = ptr[1]; // 2 + // int x2 = ptr[2]; // 0 + // int x3 = ptr[3]; // 0 + // int x4 = ptr[4]; // UB + // int x5 = ptr[-1]; // UB + const llvm::APSInt &OffsetInt = OffsetVal->getValue(); + const auto Offset = static_cast<uint64_t>(OffsetInt.getExtValue()); + // Use `getZExtValue` because array extent can not be negative. + const uint64_t Extent = CAT->getSize().getZExtValue(); + // Check for `OffsetInt < 0` but NOT for `Offset < 0`, because `OffsetInt` + // CAN be negative, but `Offset` can NOT, because `Offset` is an uint64_t. + if (OffsetInt < 0 || Offset >= Extent) + return UndefinedVal(); + // From here `Offset` is in the bounds. + + // Handle InitListExpr. + if (const auto *ILE = dyn_cast<InitListExpr>(Init)) + return getSValFromInitListExpr(ILE, Offset, R->getElementType()); + + // FIXME: Handle StringLiteral. + + // FIXME: Handle CompoundLiteralExpr. + + return None; +} + +Optional<SVal> +RegionStoreManager::getSValFromInitListExpr(const InitListExpr *ILE, + uint64_t Offset, QualType ElemT) { + assert(ILE && "InitListExpr should not be null"); + + // C++20 [expr.add] 9.4.17.5 (excerpt): + // i-th array element is value-initialized for each k < i ⤠n, + // where k is an expression-list size and n is an array extent. + if (Offset >= ILE->getNumInits()) + return svalBuilder.makeZeroVal(ElemT); + + // Return a constant value, if it is presented. + // FIXME: Support other SVals. + const Expr *E = ILE->getInit(Offset); + return svalBuilder.getConstantVal(E); +} + SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // Check if the region has a binding. @@ -1658,64 +1751,8 @@ return svalBuilder.makeIntVal(c, T); } } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) { - // Check if the containing array has an initialized value that we can trust. - // We can trust a const value or a value of a global initializer in main(). - const VarDecl *VD = VR->getDecl(); - if (VD->getType().isConstQualified() || - R->getElementType().isConstQualified() || - (B.isMainAnalysis() && VD->hasGlobalStorage())) { - if (const Expr *Init = VD->getAnyInitializer()) { - if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { - // The array index has to be known. - if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) { - // If it is not an array, return Undef. - QualType T = VD->getType(); - const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T); - if (!CAT) - return UndefinedVal(); - - // Support one-dimensional array. - // C++20 [expr.add] 7.6.6.4 (excerpt): - // If P points to an array element i of an array object x with n - // elements, where i < 0 or i > n, the behavior is undefined. - // Dereferencing is not allowed on the "one past the last - // element", when i == n. - // Example: - // const int arr[4] = {1, 2}; - // const int *ptr = arr; - // int x0 = ptr[0]; // 1 - // int x1 = ptr[1]; // 2 - // int x2 = ptr[2]; // 0 - // int x3 = ptr[3]; // 0 - // int x4 = ptr[4]; // UB - // TODO: Support multidimensional array. - if (!isa<ConstantArrayType>(CAT->getElementType())) { - // One-dimensional array. - const llvm::APSInt &Idx = CI->getValue(); - const auto I = static_cast<uint64_t>(Idx.getExtValue()); - // Use `getZExtValue` because array extent can not be negative. - const uint64_t Extent = CAT->getSize().getZExtValue(); - // Check for `Idx < 0`, NOT for `I < 0`, because `Idx` CAN be - // negative, but `I` can NOT. - if (Idx < 0 || I >= Extent) - return UndefinedVal(); - - // C++20 [expr.add] 9.4.17.5 (excerpt): - // i-th array element is value-initialized for each k < i ⤠n, - // where k is an expression-list size and n is an array extent. - if (I >= InitList->getNumInits()) - return svalBuilder.makeZeroVal(R->getElementType()); - - // Return a constant value, if it is presented. - // FIXME: Support other SVals. - const Expr *E = InitList->getInit(I); - if (Optional<SVal> V = svalBuilder.getConstantVal(E)) - return *V; - } - } - } - } - } + if (Optional<SVal> V = getConstantValFromConstArrayInitializer(B, VR, R)) + return *V; } // Check for loads from a code text region. For such loads, just give up.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits