NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a_sidorin, rnkovacs, Szelethus, 
baloghadamsoftware, Charusso.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, mikhail.ramalho, 
a.sidorin, JDevlieghere, szepet.
Herald added a project: clang.

This is inspired by https://bugs.llvm.org/show_bug.cgi?id=42780, even though it 
doesn't fix the bug.

If the global variable has an initializer, we'll ignore it because we're 
normally not analyzing the program from the beginning. This means that a global 
variable may have changed before we start our analysis.

However when we're analyzing main() as the top-level function, we can rely on 
global initializers to still be valid. At least in C; in C++ we have global 
constructors for breaking this logic. In C we can also have global constructors 
as a GNU extension, but i'd rather not worry about that.

In this patch i don't try to evaluate global initializers; the patch only 
covers the case when they're constants. This is the reason why we don't cover 
https://bugs.llvm.org/show_bug.cgi?id=42780, however we could cover it if 
`SValBuilder.getConstantVal()` was a bit more feature-rich (namely, we need to 
be able to constant-evaluate expressions like `&var.field` where `var` is a 
global variable; it's a concrete region (i.e., without symbolic base) value in 
our model, so we can make `getConstantVal()` powerful enough to emit it).

For initializers of fields and elements i'm piggybacking on @r.stahl's work to 
cover global constant initializers (D45774 <https://reviews.llvm.org/D45774>).

The main benefit of this patch is to make it harder for people to send us false 
positive reproducers that don't actually reproduce the false positive that 
they're having :)


Repository:
  rC Clang

https://reviews.llvm.org/D65361

Files:
  clang/lib/StaticAnalyzer/Core/RegionStore.cpp
  clang/test/Analysis/main.c

Index: clang/test/Analysis/main.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/main.c
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN:                    -verify %s
+
+int x = 1;
+
+struct {
+  int a, b;
+} s = {2, 3};
+
+int arr[] = {4, 5, 6};
+
+void clang_analyzer_eval(int);
+
+int main() {
+  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;
+}
Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -154,6 +154,7 @@
 class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
                                  ClusterBindings> {
   ClusterBindings::Factory *CBFactory;
+  bool IsMainAnalysis;
 
 public:
   typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>
@@ -161,22 +162,25 @@
 
   RegionBindingsRef(ClusterBindings::Factory &CBFactory,
                     const RegionBindings::TreeTy *T,
-                    RegionBindings::TreeTy::Factory *F)
+                    RegionBindings::TreeTy::Factory *F,
+                    bool IsMainAnalysis)
       : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(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<const MemRegion *, ClusterBindings>(P),
-        CBFactory(&CBFactory) {}
+        CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
 
   RegionBindingsRef add(key_type_ref K, data_type_ref D) const {
     return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
-                             *CBFactory);
+                             *CBFactory, IsMainAnalysis);
   }
 
   RegionBindingsRef remove(key_type_ref K) const {
     return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K),
-                             *CBFactory);
+                             *CBFactory, IsMainAnalysis);
   }
 
   RegionBindingsRef addBinding(BindingKey K, SVal V) const;
@@ -206,7 +210,14 @@
 
   /// Return the internal tree as a Store.
   Store asStore() const {
-    return asImmutableMap().getRootWithoutRetain();
+    static_assert(alignof(decltype(*asImmutableMap().getRootWithoutRetain())) > 1,
+                  "No room for the flag!");
+    return (Store)((uintptr_t)(asImmutableMap().getRootWithoutRetain()) |
+                   (uintptr_t)(IsMainAnalysis));
+  }
+
+  bool isMainAnalysis() const {
+    return IsMainAnalysis;
   }
 
   void printJson(raw_ostream &Out, const char *NL = "\n",
@@ -382,7 +393,12 @@
   SVal ArrayToPointer(Loc Array, QualType ElementTy) override;
 
   StoreRef getInitialStore(const LocationContext *InitLoc) override {
-    return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this);
+    bool IsMainAnalysis = false;
+    if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl()))
+      IsMainAnalysis = FD->isMain();
+    return StoreRef(RegionBindingsRef(
+        RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory),
+        CBFactory, IsMainAnalysis).asStore(), *this);
   }
 
   //===-------------------------------------------------------------------===//
@@ -608,9 +624,10 @@
   //===------------------------------------------------------------------===//
 
   RegionBindingsRef getRegionBindings(Store store) const {
-    return RegionBindingsRef(CBFactory,
-                             static_cast<const RegionBindings::TreeTy*>(store),
-                             RBFactory.getTreeFactory());
+    return RegionBindingsRef(
+        CBFactory,
+        (const RegionBindings::TreeTy *)((uintptr_t)store & ~(uintptr_t)1),
+        RBFactory.getTreeFactory(), (bool)((uintptr_t)store & (uintptr_t)1));
   }
 
   void printJson(raw_ostream &Out, Store S, const char *NL = "\n",
@@ -1674,7 +1691,9 @@
     // Check if the containing array is const and has an initialized value.
     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<InitListExpr>(Init)) {
           // The array index has to be known.
@@ -1764,7 +1783,8 @@
     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())
+    if (RecordVarTy.isConstQualified() || Ty.isConstQualified() ||
+        (B.isMainAnalysis() && VD->hasGlobalStorage()))
       if (const Expr *Init = VD->getAnyInitializer())
         if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
           if (Index < InitList->getNumInits()) {
@@ -1982,6 +2002,11 @@
   if (isa<GlobalsSpaceRegion>(MS)) {
     QualType T = VD->getType();
 
+    if (B.isMainAnalysis())
+      if (const Expr *Init = VD->getAnyInitializer())
+        if (Optional<SVal> 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().
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to