[clang] efa7df1 - [Analyzer] Track RValue expressions

Gabor Marton via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 30 05:57:26 PDT 2021


Author: Gabor Marton
Date: 2021-03-30T14:48:38+02:00
New Revision: efa7df1682c2859dabe3646ee7dc01e68629417f

URL: https://github.com/llvm/llvm-project/commit/efa7df1682c2859dabe3646ee7dc01e68629417f
DIFF: https://github.com/llvm/llvm-project/commit/efa7df1682c2859dabe3646ee7dc01e68629417f.diff

LOG: [Analyzer] Track RValue expressions

It makes sense to track rvalue expressions in the case of special
concrete integer values. The most notable special value is zero (later
we may find other values). By tracking the origin of 0, we can provide a
better explanation for users e.g. in case of division by 0 warnings.
When the divisor is a product of a multiplication then now we can show
which operand (or both) was (were) zero and why.

Differential Revision: https://reviews.llvm.org/D99344

Added: 
    clang/test/Analysis/division-by-zero-track-zero.c
    clang/test/Analysis/division-by-zero-track-zero.cpp

Modified: 
    clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
    clang/test/Analysis/nullptr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 0edd6e3f731b6..fd334b0bc9c36 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1924,6 +1924,44 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
   return N;
 }
 
+/// Attempts to add visitors to track an RValue expression back to its point of
+/// origin. Works similarly to trackExpressionValue, but accepts only RValues.
+static void trackRValueExpression(const ExplodedNode *InputNode, const Expr *E,
+                                  PathSensitiveBugReport &report,
+                                  bugreporter::TrackingKind TKind,
+                                  bool EnableNullFPSuppression) {
+  assert(E->isRValue() && "The expression is not an rvalue!");
+  const ExplodedNode *RVNode = findNodeForExpression(InputNode, E);
+  if (!RVNode)
+    return;
+  ProgramStateRef RVState = RVNode->getState();
+  SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext());
+  const auto *BO = dyn_cast<BinaryOperator>(E);
+  if (!BO)
+    return;
+  if (!V.isZeroConstant())
+    return;
+  if (!BO->isMultiplicativeOp())
+    return;
+
+  SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext());
+  SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext());
+
+  // Track both LHS and RHS of a multiplication.
+  if (BO->getOpcode() == BO_Mul) {
+    if (LHSV.isZeroConstant())
+      trackExpressionValue(InputNode, BO->getLHS(), report, TKind,
+                           EnableNullFPSuppression);
+    if (RHSV.isZeroConstant())
+      trackExpressionValue(InputNode, BO->getRHS(), report, TKind,
+                           EnableNullFPSuppression);
+  } else { // Track only the LHS of a division or a modulo.
+    if (LHSV.isZeroConstant())
+      trackExpressionValue(InputNode, BO->getLHS(), report, TKind,
+                           EnableNullFPSuppression);
+  }
+}
+
 bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
                                        const Expr *E,
                                        PathSensitiveBugReport &report,
@@ -2069,6 +2107,11 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
             loc::MemRegionVal(RegionRVal), /*assumption=*/false));
     }
   }
+
+  if (Inner->isRValue())
+    trackRValueExpression(LVNode, Inner, report, TKind,
+                          EnableNullFPSuppression);
+
   return true;
 }
 

diff  --git a/clang/test/Analysis/division-by-zero-track-zero.c b/clang/test/Analysis/division-by-zero-track-zero.c
new file mode 100644
index 0000000000000..f6b2a78ed7017
--- /dev/null
+++ b/clang/test/Analysis/division-by-zero-track-zero.c
@@ -0,0 +1,11 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core \
+// RUN:   -analyzer-output=text \
+// RUN:   -verify %s
+
+int track_mul_lhs_0(int x, int y) {
+  int p0 = x < 0;   // expected-note {{Assuming 'x' is >= 0}} \
+                    // expected-note {{'p0' initialized to 0}}
+  int div = p0 * y; // expected-note {{'div' initialized to 0}}
+  return 1 / div;   // expected-note {{Division by zero}} \
+                    // expected-warning {{Division by zero}}
+}

diff  --git a/clang/test/Analysis/division-by-zero-track-zero.cpp b/clang/test/Analysis/division-by-zero-track-zero.cpp
new file mode 100644
index 0000000000000..c4b9550c76c0f
--- /dev/null
+++ b/clang/test/Analysis/division-by-zero-track-zero.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core \
+// RUN:   -analyzer-output=text \
+// RUN:   -verify %s
+
+namespace test_tracking_of_lhs_multiplier {
+  int f(int x, int y) {
+    bool p0 = x < 0;  // expected-note {{Assuming 'x' is >= 0}} \
+                      // expected-note {{'p0' initialized to 0}}
+    int div = p0 * y; // expected-note {{'div' initialized to 0}}
+    return 1 / div;   // expected-note {{Division by zero}} \
+                      // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_lhs_multiplier
+
+namespace test_tracking_of_rhs_multiplier {
+  int f(int x, int y) {
+    bool p0 = x < 0;  // expected-note {{Assuming 'x' is >= 0}} \
+                      // expected-note {{'p0' initialized to 0}}
+    int div = y * p0; // expected-note {{'div' initialized to 0}}
+    return 1 / div;   // expected-note {{Division by zero}} \
+                      // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_rhs_multiplier
+
+namespace test_tracking_of_nested_multiplier {
+  int f(int x, int y, int z) {
+    bool p0 = x < 0;  // expected-note {{Assuming 'x' is >= 0}} \
+                      // expected-note {{'p0' initialized to 0}}
+    int div = y*z*p0; // expected-note {{'div' initialized to 0}}
+    return 1 / div;   // expected-note {{Division by zero}} \
+                      // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_nested_multiplier
+
+namespace test_tracking_through_multiple_stmts {
+  int f(int x, int y) {
+    bool p0 = x < 0;      // expected-note {{Assuming 'x' is >= 0}}
+    bool p1 = p0 ? 0 : 1; // expected-note {{'p0' is false}} \
+                          // expected-note {{'?' condition is false}}
+    bool p2 = 1 - p1;     // expected-note {{'p2' initialized to 0}}
+    int div = p2 * y;     // expected-note {{'div' initialized to 0}}
+    return 1 / div;       // expected-note {{Division by zero}} \
+                          // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_through_multiple_stmts
+
+namespace test_tracking_both_lhs_and_rhs {
+  int f(int x, int y) {
+    bool p0 = x < 0;   // expected-note {{Assuming 'x' is >= 0}} \
+                       // expected-note {{'p0' initialized to 0}}
+    bool p1 = y < 0;   // expected-note {{Assuming 'y' is >= 0}} \
+                       // expected-note {{'p1' initialized to 0}}
+    int div = p0 * p1; // expected-note {{'div' initialized to 0}}
+    return 1 / div;    // expected-note {{Division by zero}} \
+                       // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_both_lhs_and_rhs
+
+namespace test_tracking_of_multiplier_and_parens {
+  int f(int x, int y, int z) {
+    bool p0 = x < 0;    // expected-note {{Assuming 'x' is >= 0}} \
+                        // expected-note {{'p0' initialized to 0}}
+    int div = y*(z*p0); // expected-note {{'div' initialized to 0}}
+    return 1 / div;     // expected-note {{Division by zero}} \
+                        // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_multiplier_and_parens
+
+namespace test_tracking_of_divisible {
+  int f(int x, int y) {
+    bool p0 = x < 0;    // expected-note {{Assuming 'x' is >= 0}} \
+                        // expected-note {{'p0' initialized to 0}}
+    int div = p0 / y;   // expected-note {{'div' initialized to 0}}
+    return 1 / div;     // expected-note {{Division by zero}} \
+                        // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_divisible
+
+namespace test_tracking_of_modulo {
+  int f(int x, int y) {
+    bool p0 = x < 0;    // expected-note {{Assuming 'x' is >= 0}} \
+                        // expected-note {{'p0' initialized to 0}}
+    int div = p0 % y;   // expected-note {{'div' initialized to 0}}
+    return 1 / div;     // expected-note {{Division by zero}} \
+                        // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_modulo
+
+namespace test_tracking_of_assignment {
+  int f(int x) {
+    bool p0 = x < 0;    // expected-note {{Assuming 'x' is >= 0}} \
+                        // expected-note {{'p0' initialized to 0}}
+    int div = 1;
+    div *= p0;          // expected-note {{The value 0 is assigned to 'div'}}
+    return 1 / div;     // expected-note {{Division by zero}} \
+                        // expected-warning {{Division by zero}}
+  }
+} // namespace test_tracking_of_assignment

diff  --git a/clang/test/Analysis/nullptr.cpp b/clang/test/Analysis/nullptr.cpp
index e9b975c148aa6..24b574a4ccfe2 100644
--- a/clang/test/Analysis/nullptr.cpp
+++ b/clang/test/Analysis/nullptr.cpp
@@ -64,7 +64,7 @@ void zoo1backwards() {
 
 typedef __INTPTR_TYPE__ intptr_t;
 void zoo1multiply() {
-  char **p = 0; // FIXME-should-be-note:{{'p' initialized to a null pointer value}}
+  char **p = 0; // expected-note{{'p' initialized to a null pointer value}}
   delete *((char **)((intptr_t)p * 2)); // expected-warning{{Dereference of null pointer}}
                    // expected-note at -1{{Dereference of null pointer}}
 }


        


More information about the cfe-commits mailing list