Index: cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -155,28 +155,42 @@ ClusterBindings> { ClusterBindings::Factory *CBFactory; + // This flag indicates whether the current bindings are within the analysis + // that has started from main(). It affects how we perform loads from + // global variables that have initializers: if we have observed the + // program execution from the start and we know that these variables + // have not been overwritten yet, we can be sure that their initializers + // are still relevant. This flag never gets changed when the bindings are + // updated, so it could potentially be moved into RegionStoreManager + // (as if it's the same bindings but a different loading procedure) + // however that would have made the manager needlessly stateful. + bool IsMainAnalysis; + public: typedef llvm::ImmutableMapRef ParentTy; RegionBindingsRef(ClusterBindings::Factory &CBFactory, const RegionBindings::TreeTy *T, - RegionBindings::TreeTy::Factory *F) + RegionBindings::TreeTy::Factory *F, + bool IsMainAnalysis) : llvm::ImmutableMapRef(T, F), - CBFactory(&CBFactory) {} + CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {} - RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) + RegionBindingsRef(const ParentTy &P, + ClusterBindings::Factory &CBFactory, + bool IsMainAnalysis) : llvm::ImmutableMapRef(P), - CBFactory(&CBFactory) {} + CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {} RegionBindingsRef add(key_type_ref K, data_type_ref D) const { return RegionBindingsRef(static_cast(this)->add(K, D), - *CBFactory); + *CBFactory, IsMainAnalysis); } RegionBindingsRef remove(key_type_ref K) const { return RegionBindingsRef(static_cast(this)->remove(K), - *CBFactory); + *CBFactory, IsMainAnalysis); } RegionBindingsRef addBinding(BindingKey K, SVal V) const; @@ -206,7 +220,13 @@ /// Return the internal tree as a Store. Store asStore() const { - return asImmutableMap().getRootWithoutRetain(); + llvm::PointerIntPair Ptr = { + asImmutableMap().getRootWithoutRetain(), IsMainAnalysis}; + return reinterpret_cast(Ptr.getOpaqueValue()); + } + + bool isMainAnalysis() const { + return IsMainAnalysis; } void printJson(raw_ostream &Out, const char *NL = "\n", @@ -381,8 +401,15 @@ /// casts from arrays to pointers. SVal ArrayToPointer(Loc Array, QualType ElementTy) override; + /// Creates the Store that correctly represents memory contents before + /// the beginning of the analysis of the given top-level stack frame. StoreRef getInitialStore(const LocationContext *InitLoc) override { - return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this); + bool IsMainAnalysis = false; + if (const auto *FD = dyn_cast(InitLoc->getDecl())) + IsMainAnalysis = FD->isMain() && !Ctx.getLangOpts().CPlusPlus; + return StoreRef(RegionBindingsRef( + RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory), + CBFactory, IsMainAnalysis).asStore(), *this); } //===-------------------------------------------------------------------===// @@ -608,9 +635,13 @@ //===------------------------------------------------------------------===// RegionBindingsRef getRegionBindings(Store store) const { - return RegionBindingsRef(CBFactory, - static_cast(store), - RBFactory.getTreeFactory()); + llvm::PointerIntPair Ptr; + Ptr.setFromOpaqueValue(const_cast(store)); + return RegionBindingsRef( + CBFactory, + static_cast(Ptr.getPointer()), + RBFactory.getTreeFactory(), + Ptr.getInt()); } void printJson(raw_ostream &Out, Store S, const char *NL = "\n", @@ -1671,10 +1702,12 @@ return svalBuilder.makeIntVal(c, T); } } else if (const VarRegion *VR = dyn_cast(superR)) { - // Check if the containing array is const and has an initialized value. + // 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(); - // Either the array or the array element has to be const. - if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { + 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. @@ -1763,8 +1796,11 @@ const VarDecl *VD = VR->getDecl(); QualType RecordVarTy = VD->getType(); unsigned Index = FD->getFieldIndex(); - // Either the record variable or the field has to be const qualified. - if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) + // Either the record variable or the field has an initializer that we can + // trust. We trust initializers of constants and, additionally, respect + // initializers of globals when analyzing main(). + if (RecordVarTy.isConstQualified() || Ty.isConstQualified() || + (B.isMainAnalysis() && VD->hasGlobalStorage())) if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast(Init)) { if (Index < InitList->getNumInits()) { @@ -1982,6 +2018,12 @@ if (isa(MS)) { QualType T = VD->getType(); + // If we're in main(), then global initializers have not become stale yet. + if (B.isMainAnalysis()) + if (const Expr *Init = VD->getAnyInitializer()) + if (Optional V = svalBuilder.getConstantVal(Init)) + return *V; + // Function-scoped static variables are default-initialized to 0; if they // have an initializer, it would have been processed by now. // FIXME: This is only true when we're starting analysis from main(). Index: cfe/trunk/test/Analysis/main.c =================================================================== --- cfe/trunk/test/Analysis/main.c +++ cfe/trunk/test/Analysis/main.c @@ -0,0 +1,32 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +int x = 1; + +struct { + int a, b; +} s = {2, 3}; + +int arr[] = {4, 5, 6}; + +void clang_analyzer_eval(int); + +int main() { + // In main() we know that the initial values are still valid. + clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(s.a == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(s.b == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(arr[0] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(arr[1] == 5); // expected-warning{{TRUE}} + clang_analyzer_eval(arr[2] == 6); // expected-warning{{TRUE}} + return 0; +} + +void foo() { + // In other functions these values may already be overwritten. + clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(s.a == 2); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(s.b == 3); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(arr[0] == 4); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(arr[1] == 5); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(arr[2] == 6); // expected-warning{{TRUE}} // expected-warning{{FALSE}} +} Index: cfe/trunk/test/Analysis/main.cpp =================================================================== --- cfe/trunk/test/Analysis/main.cpp +++ cfe/trunk/test/Analysis/main.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +int x = 1; + +struct { + int a, b; +} s = {2, 3}; + +int arr[] = {4, 5, 6}; + +void clang_analyzer_eval(int); + +int main() { + // Do not trust global initializers in C++. + clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(s.a == 2); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(s.b == 3); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(arr[0] == 4); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(arr[1] == 5); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + clang_analyzer_eval(arr[2] == 6); // expected-warning{{TRUE}} // expected-warning{{FALSE}} + return 0; +}