Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1334,6 +1334,7 @@ return nullptr; } +// FIXME: Support unsigned offset as well. RegionRawOffset ElementRegion::getAsArrayOffset() const { int64_t offset = 0; const ElementRegion *ER = this; Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -437,6 +437,12 @@ RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, const SubRegion *R); + Optional getConstantValFromConstArrayInitializer( + RegionBindingsConstRef B, const VarRegion *VR, const llvm::APSInt &Idx, + QualType ElemT); + Optional getSValFromInitListExprByIndex(const InitListExpr *ILE, + const llvm::APSInt &Idx, + QualType ElemT); public: // Part of public interface to class. @@ -1624,6 +1630,54 @@ return Result; } +Optional RegionStoreManager::getConstantValFromConstArrayInitializer( + RegionBindingsConstRef B, const VarRegion *VR, const llvm::APSInt &Idx, + QualType ElemT) { + // Array should be immutable. + const VarDecl *VD = VR->getDecl(); + if (!VD->getType().isConstQualified() && !ElemT.isConstQualified() && + (!B.isMainAnalysis() || !VD->hasGlobalStorage())) + return None; + + // Array should have an initializer. + const Expr *Init = VD->getAnyInitializer(); + + if (!Init) + return None; + + // Handle InitListExpr. + if (const auto *ILE = dyn_cast(Init)) + return getSValFromInitListExprByIndex(ILE, Idx, ElemT); + + // FIXME: Handle StringLiteral. + + // FIXME: Handle CompoundLiteralExpr. + + return None; +} + +Optional RegionStoreManager::getSValFromInitListExprByIndex( + const InitListExpr *ILE, const llvm::APSInt &Idx, QualType ElemT) { + assert(ILE && "InitListExpr should not be null"); + // We shall care about signed negative indexes according to + // `getExprForConstArrayByRawIndex` contract. + // If index is out of bounds. Return Undef. + if (Idx < 0) + return UndefinedVal(); + // Get an actual Expr by index. + const int64_t I = Idx.getExtValue(); + const Expr *E = + ILE->getExprForConstArrayByRawIndex(Ctx, static_cast(I)); + // If E is null, then the index is out of bounds. Return Undef. + if (!E) + return UndefinedVal(); + // If E is the same as InitList, then there is no value specified + // in the list and we shall return a zero value. + if (E == ILE) + return svalBuilder.makeZeroVal(ElemT); + // Return a constant value. + return svalBuilder.getConstantVal(E); +} SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { @@ -1657,45 +1711,33 @@ char c = (i >= length) ? '\0' : Str->getCodeUnit(i); return svalBuilder.makeIntVal(c, T); } - } else if (const VarRegion *VR = dyn_cast(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(Init)) { - // The array index has to be known. - if (auto CI = R->getIndex().getAs()) { - const llvm::APSInt &Int = CI->getValue(); - int64_t Idx = Int.getExtValue(); - - // We shall care about signed negative indexes according to - // `getExprForConstArrayByRawIndex` contract. - // If index is out of bounds. Return Undef. - if (Int.isSigned() && Idx < 0) - return UndefinedVal(); - - // Get an actual Expr by index. - const Expr *E = InitList->getExprForConstArrayByRawIndex( - Ctx, static_cast(Idx)); - - // If E is null, then the index is out of bounds. Return Undef. - if (!E) - return UndefinedVal(); - - // If E is the same as InitList, then there is no value specified - // in the list and we shall return a zero value. - if (E == InitList) - return svalBuilder.makeZeroVal(R->getElementType()); - - // Return a constant value. - if (Optional V = svalBuilder.getConstantVal(E)) - return *V; - } - } + } else if (isa(superR) || isa(superR)) { + const VarRegion *VR = nullptr; + // Prepare APSInt as int64_t. + llvm::APSInt Idx(64, false); + if (isa(superR)) { + // Check if it is a mutli-dimensional array. + const RegionRawOffset &RRO = R->getAsArrayOffset(); + VR = dyn_cast_or_null(RRO.getRegion()); + if (VR) { + // FIXME: We are limited here due to RegionRawOffset only contains + // offset as signed integer. Adjust the code when + // ElementRegion::getAsArrayOffset supports unsigned offsets. + Idx = RRO.getOffset().getQuantity() / + Ctx.getTypeSizeInChars(R->getElementType()).getQuantity(); } + } else if (auto CI = R->getIndex().getAs()) { + // Check if it is a single-dimensional array. + VR = cast(superR); + const llvm::APSInt Int = CI->getValue(); + Idx = Int.getExtValue(); + Idx.setIsUnsigned(Int.isUnsigned()); + } + + if (VR) { + if (Optional V = getConstantValFromConstArrayInitializer( + B, VR, Idx, R->getElementType())) + return *V; } } @@ -1715,8 +1757,7 @@ if (!O.getRegion()) return UnknownVal(); - if (const TypedValueRegion *baseR = - dyn_cast_or_null(O.getRegion())) { + if (const auto *baseR = dyn_cast(O.getRegion())) { QualType baseT = baseR->getValueType(); if (baseT->isScalarType()) { QualType elemT = R->getElementType(); Index: clang/test/Analysis/initialization.c =================================================================== --- clang/test/Analysis/initialization.c +++ clang/test/Analysis/initialization.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.builtin,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection -verify %s void clang_analyzer_eval(int); @@ -26,3 +26,34 @@ clang_analyzer_eval(sm.a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(sm.b == 0); // expected-warning{{TRUE}} } + +const int glob_arr1[3][3] = {[0][0] = 1, [1][1] = 5, [2][0] = 7}; +void glob_arr_index1() { + clang_analyzer_eval(glob_arr1[0][0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][1] == 5); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[2][0] == 7); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[2][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[2][2] == 0); // expected-warning{{TRUE}} +} + +void negative_index1() { + int x = 2, y = -2; + clang_analyzer_eval(glob_arr1[x][y] == 5); // expected-warning{{TRUE}} + x = 3; + y = -3; + clang_analyzer_eval(glob_arr1[x][y] == 7); // expected-warning{{TRUE}} +} + +void out_of_bound_index1() { + int x = -1, y = -1; + int res = glob_arr1[x][y]; // expected-warning{{garbage or undefined}} +} + +void out_of_bound_index2() { + int x = 3, y = 2; + int res = glob_arr1[x][y]; // expected-warning{{garbage or undefined}} +} Index: clang/test/Analysis/initialization.cpp =================================================================== --- clang/test/Analysis/initialization.cpp +++ clang/test/Analysis/initialization.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection,core.uninitialized.UndefReturn -verify %s +// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection -verify %s void clang_analyzer_eval(int); @@ -15,8 +15,7 @@ int const arr[2][2] = {}; void arr2init() { int i = 1; - // FIXME: Should recognize that it is 0. - clang_analyzer_eval(arr[i][0]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(arr[i][0]); // expected-warning{{FALSE}} } void direct_index1() { @@ -124,8 +123,86 @@ auto buf_p = rvp[4]; // no-warning (garbage or undefined) } -const float floats[] = { - 0.0000, 0.0235, 0.0470, 0.0706, 0.0941, 0.1176}; -float no_warn_garbage_value() { - return floats[0]; // no-warning (garbage or undefined) +const int glob_arr1[2][2][3] = {{{1, 2}}, {{7}}}; +void glob_arr_index1() { + clang_analyzer_eval(glob_arr1[0][0][0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][0][1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][0][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][1][0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][1][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[0][1][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][0][0] == 7); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][0][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][0][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][1][0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][1][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1][1][2] == 0); // expected-warning{{TRUE}} +} + +int const glob_arr2[2][2][3] = {{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}}; +void glob_arr_index2() { + clang_analyzer_eval(glob_arr2[0][0][0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[0][0][1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[0][0][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[0][1][0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[0][1][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[0][1][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[1][0][0] == 7); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[1][0][1] == 8); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[1][0][2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[1][1][0] == 10); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[1][1][1] == 11); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr2[1][1][2] == 12); // expected-warning{{TRUE}} +} + +const int glob_arr3[4][2] = {{}, {3}, {}, {7}}; +void glob_arr_index3() { + clang_analyzer_eval(glob_arr3[0][0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[0][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[1][0] == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[1][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[2][0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[2][1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[3][0] == 7); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr3[3][1] == 0); // expected-warning{{TRUE}} +} + +void negative_index1() { + int x = 2, y = -2; + clang_analyzer_eval(glob_arr3[x][y] == 3); // expected-warning{{TRUE}} + x = 4; + y = -2; + clang_analyzer_eval(glob_arr3[x][y] == 7); // expected-warning{{TRUE}} +} + +void out_of_bound_index1() { + int x = -3, y = 2; + int res = glob_arr3[x][y]; // expected-warning{{garbage or undefined}} +} + +void out_of_bound_index2() { + int x = 3, y = 2; + int res = glob_arr3[x][y]; // expected-warning{{garbage or undefined}} +} + +const int glob_arr4[8] = {1, 2, 3, 4}; +void glob_arr_index4() { + clang_analyzer_eval(glob_arr4[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[2] == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[3] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[4] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[5] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[6] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr4[7] == 0); // expected-warning{{TRUE}} +} + +void out_of_bound_index3() { + int x = -42; + int res = glob_arr4[x]; // expected-warning{{garbage or undefined}} +} + +void out_of_bound_index4() { + int x = 42; + int res = glob_arr4[x]; // expected-warning{{garbage or undefined}} }