diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 2d86c779ba850..678b07ebba1b0 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1196,7 +1196,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, } SVal InitVal; - if (Init->getType()->isArrayType()) { + if (Field->getType()->isArrayType()) { // Handle arrays of trivial type. We can represent this with a // primitive load/copy from the base array region. const ArraySubscriptExpr *ASE; diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 0f7e03ce50858..01c792a9011f9 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -2726,8 +2726,16 @@ RegionStoreManager::bindArray(LimitedRegionBindingsConstRef B, return bindAggregate(B, R, Init); } - if (isa(Init)) + // We may get non-CompoundVal accidentally due to imprecise cast logic or + // that we are binding a genuinely symbolic/unknown/undefined array value. + // Preserve Init as a default binding rather than lossily converting to + // UnknownVal(), and handle every non-CompoundVal case exhaustively (like + // bindStruct()/bindVector() do) rather than enumerating specific SVal + // kinds one at a time. + if (!isa(Init)) { + assert((isa(Init))); return bindAggregate(B, R, Init); + } // Remaining case: explicit compound values. const nonloc::CompoundVal& CV = Init.castAs(); diff --git a/clang/test/Analysis/initializer.cpp b/clang/test/Analysis/initializer.cpp index 88758f7c3ac1d..dd0c91b7ead61 100644 --- a/clang/test/Analysis/initializer.cpp +++ b/clang/test/Analysis/initializer.cpp @@ -628,8 +628,7 @@ struct A { void test1() { A a; clang_analyzer_eval(a.m_buf[0] == 0); // expected-warning{{TRUE}} - // FIXME The next eval should result in TRUE. - clang_analyzer_eval(*a.m_ptr == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(*a.m_ptr == 0); // expected-warning{{TRUE}} } void test2() { @@ -646,9 +645,8 @@ void test3() { void test3Bis(char arg) { A a(arg); - // FIXME This test should behave like test3. - clang_analyzer_eval(a.m_buf[0] == arg); // expected-warning{{FALSE}} // expected-warning{{TRUE}} - clang_analyzer_eval(*a.m_ptr == arg); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a.m_buf[0] == arg); // expected-warning{{TRUE}} + clang_analyzer_eval(*a.m_ptr == arg); // expected-warning{{TRUE}} } void test4(char arg) { diff --git a/clang/test/Analysis/issue-210183.cpp b/clang/test/Analysis/issue-210183.cpp new file mode 100644 index 0000000000000..a9c2ff24bccb8 --- /dev/null +++ b/clang/test/Analysis/issue-210183.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux-gnu -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s + +// https://github.com/llvm/llvm-project/issues/210183 +// +// A pointer member initialized via array-to-pointer decay of a +// reference-to-array constructor parameter used to be modeled as the +// address of the whole array (instead of its first element). Dereferencing +// and storing through that mistyped pointer then reached +// RegionStoreManager::bindArray() with a scalar Init value, crashing on an +// unchecked castAs(). + +template void clang_analyzer_dump(T); + +template struct Span { + template + Span(T (&arr)[N]) : ptr_(arr) {} + T *data() { return ptr_; } + T *ptr_; +}; + +char *ptr(); +char buffer[10]; + +void test() { + char *p = Span(buffer).data(); + + // p and buffer must resolve to the same address: the first element of + // buffer, not the whole array. + clang_analyzer_dump(buffer); // expected-warning{{&Element{buffer,0 S64b,char}}} + clang_analyzer_dump(p); // expected-warning{{&Element{buffer,0 S64b,char}}} + + int v = (int)(long)ptr(); + *p = v; // no-crash +}