NoQ created this revision.

When encountering an array-to-pointer-decay and the array base is null (or any 
other concrete pointer value) (eg. it's a member array in a structure, and the 
structure pointer is null; of course it wouldn't happen to stack-based or 
global arrays), do not yield UnknownVal; instead, yield that concrete value.

While obvious, this change now triggers false positives because our suppression 
for inlined defensive checks was not prepared for dealing with array subscripts 
(the `idcTrackZeroValueThroughUnaryPointerOperatorsWithArrayField` test in 
`inlining/inline-defensive-checks.cpp` starts failing). So i additionally 
improve the suppression.

As discussed in https://reviews.llvm.org/D31982, which added the aforementioned 
test case, `bugreporter::getDerefExpr()` should have been used (we only used to 
match member expressions earlier, but now that we encountered arrays, we could 
use all the features it function can offer). Now that the code uses that 
function, and a few issues within that function were further fixed in order to 
support the new use case and avoid regressions.


https://reviews.llvm.org/D32291

Files:
  lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/null-deref-offsets.c


Index: test/Analysis/null-deref-offsets.c
===================================================================
--- test/Analysis/null-deref-offsets.c
+++ test/Analysis/null-deref-offsets.c
@@ -7,7 +7,7 @@
   int z[2];
 };
 
-void testOffsets(struct S *s) {
+void testOffsets(struct S *s, int coin) {
   if (s != 0)
     return;
 
@@ -21,14 +21,17 @@
 
   // FIXME: These should ideally be true.
   clang_analyzer_eval(&(s->y) == 4); // expected-warning{{FALSE}}
-  clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{FALSE}}
+  clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{FALSE}}
 
   // FIXME: These should ideally be false.
   clang_analyzer_eval(&(s->y) == 0); // expected-warning{{TRUE}}
-  clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{UNKNOWN}}
-
-  // But this should still be a null dereference.
-  s->y = 5; // expected-warning{{Access to field 'y' results in a dereference 
of a null pointer (loaded from variable 's')}}
+  clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{TRUE}}
+
+  // But these should still be reported as null dereferences.
+  if (coin)
+    s->y = 5; // expected-warning{{Access to field 'y' results in a 
dereference of a null pointer (loaded from variable 's')}}
+  else
+    s->z[1] = 6; // expected-warning{{Array access (via field 'z') results in 
a null pointer dereference}}
 }
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1338,6 +1338,9 @@
 ///  the array).  This is called by ExprEngine when evaluating casts
 ///  from arrays to pointers.
 SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
+  if (Array.getAs<loc::ConcreteInt>())
+    return Array;
+
   if (!Array.getAs<loc::MemRegionVal>())
     return UnknownVal();
 
Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===================================================================
--- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -61,7 +61,9 @@
         return U->getSubExpr()->IgnoreParenCasts();
     }
     else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
-      if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) {
+      if (ME->isImplicitAccess()) {
+        return ME;
+      } else if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) {
         return ME->getBase()->IgnoreParenCasts();
       } else {
         // If we have a member expr with a dot, the base must have been
@@ -73,9 +75,9 @@
       return IvarRef->getBase()->IgnoreParenCasts();
     }
     else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) {
-      return AE->getBase();
+      return getDerefExpr(AE->getBase());
     }
-    else if (isDeclRefExprToReference(E)) {
+    else if (isa<DeclRefExpr>(E)) {
       return E;
     }
     break;
@@ -974,14 +976,11 @@
     // This code interacts heavily with this hack; otherwise the value
     // would not be null at all for most fields, so we'd be unable to track it.
     if (const auto *Op = dyn_cast<UnaryOperator>(Ex))
-      if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) {
-        Ex = Op->getSubExpr()->IgnoreParenCasts();
-        while (const auto *ME = dyn_cast<MemberExpr>(Ex)) {
-          Ex = ME->getBase()->IgnoreParenCasts();
-        }
-      }
+      if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue())
+        if (const Expr *DerefEx = getDerefExpr(Op->getSubExpr()))
+          Ex = DerefEx;
 
-    if (ExplodedGraph::isInterestingLValueExpr(Ex) || 
CallEvent::isCallStmt(Ex))
+    if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) || 
CallEvent::isCallStmt(Ex)))
       Inner = Ex;
   }
 


Index: test/Analysis/null-deref-offsets.c
===================================================================
--- test/Analysis/null-deref-offsets.c
+++ test/Analysis/null-deref-offsets.c
@@ -7,7 +7,7 @@
   int z[2];
 };
 
-void testOffsets(struct S *s) {
+void testOffsets(struct S *s, int coin) {
   if (s != 0)
     return;
 
@@ -21,14 +21,17 @@
 
   // FIXME: These should ideally be true.
   clang_analyzer_eval(&(s->y) == 4); // expected-warning{{FALSE}}
-  clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{FALSE}}
+  clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{FALSE}}
 
   // FIXME: These should ideally be false.
   clang_analyzer_eval(&(s->y) == 0); // expected-warning{{TRUE}}
-  clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{UNKNOWN}}
-
-  // But this should still be a null dereference.
-  s->y = 5; // expected-warning{{Access to field 'y' results in a dereference of a null pointer (loaded from variable 's')}}
+  clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{TRUE}}
+
+  // But these should still be reported as null dereferences.
+  if (coin)
+    s->y = 5; // expected-warning{{Access to field 'y' results in a dereference of a null pointer (loaded from variable 's')}}
+  else
+    s->z[1] = 6; // expected-warning{{Array access (via field 'z') results in a null pointer dereference}}
 }
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1338,6 +1338,9 @@
 ///  the array).  This is called by ExprEngine when evaluating casts
 ///  from arrays to pointers.
 SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
+  if (Array.getAs<loc::ConcreteInt>())
+    return Array;
+
   if (!Array.getAs<loc::MemRegionVal>())
     return UnknownVal();
 
Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
===================================================================
--- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -61,7 +61,9 @@
         return U->getSubExpr()->IgnoreParenCasts();
     }
     else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
-      if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) {
+      if (ME->isImplicitAccess()) {
+        return ME;
+      } else if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) {
         return ME->getBase()->IgnoreParenCasts();
       } else {
         // If we have a member expr with a dot, the base must have been
@@ -73,9 +75,9 @@
       return IvarRef->getBase()->IgnoreParenCasts();
     }
     else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) {
-      return AE->getBase();
+      return getDerefExpr(AE->getBase());
     }
-    else if (isDeclRefExprToReference(E)) {
+    else if (isa<DeclRefExpr>(E)) {
       return E;
     }
     break;
@@ -974,14 +976,11 @@
     // This code interacts heavily with this hack; otherwise the value
     // would not be null at all for most fields, so we'd be unable to track it.
     if (const auto *Op = dyn_cast<UnaryOperator>(Ex))
-      if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) {
-        Ex = Op->getSubExpr()->IgnoreParenCasts();
-        while (const auto *ME = dyn_cast<MemberExpr>(Ex)) {
-          Ex = ME->getBase()->IgnoreParenCasts();
-        }
-      }
+      if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue())
+        if (const Expr *DerefEx = getDerefExpr(Op->getSubExpr()))
+          Ex = DerefEx;
 
-    if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex))
+    if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)))
       Inner = Ex;
   }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to