Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Pushed to trunk as r16-7671-g761e0d3d785794.

gcc/analyzer/ChangeLog:
        PR analyzer/124195
        * region-model-manager.cc
        (region_model_manager::maybe_fold_binop): Bulletproof against
        division by zero by returning "unknown".
        * region-model.cc (region_model::get_gassign_result): Bail out for
        division by zero.
        * svalue.cc (svalue::maybe_get_value_range): Rename to...
        (svalue::maybe_get_value_range_1): ...this.
        (constant_svalue::maybe_get_value_range): Rename to...
        (constant_svalue::maybe_get_value_range_1): ...this.
        (unknown_svalue::maybe_get_value_range): Rename to...
        (unknown_svalue::maybe_get_value_range_1): ...this.
        (unaryop_svalue::maybe_get_value_range): Rename to...
        (unaryop_svalue::maybe_get_value_range_1): ...this.
        (binop_svalue::maybe_get_value_range): Rename to...
        (binop_svalue::maybe_get_value_range_1): ...this.
        * svalue.h (svalue::maybe_get_value_range): Reimplement inline,
        asserting that if we get a result it is not undefined_p.  Use
        maybe_get_value_range_1 for the vfunc.
        (svalue::maybe_get_value_range_1): Rename from above.
        (constant_svalue::maybe_get_value_range): Rename to...
        (constant_svalue::maybe_get_value_range_1): ...this.
        (unknown_svalue::maybe_get_value_range): Rename to...
        (unknown_svalue::maybe_get_value_range_1): ...this.
        (unaryop_svalue::maybe_get_value_range): Rename to...
        (unaryop_svalue::maybe_get_value_range_1): ...this.
        (binop_svalue::maybe_get_value_range): Rename to...
        (binop_svalue::maybe_get_value_range_1): ...this.

gcc/testsuite/ChangeLog:
        PR analyzer/124195
        * c-c++-common/analyzer/divide-by-zero-1.c: New test.
        * c-c++-common/analyzer/divide-by-zero-pr124195-2.c: New test.
        * gcc.dg/analyzer/divide-by-zero-pr124195-1.c: New test.

Signed-off-by: David Malcolm <[email protected]>
---
 gcc/analyzer/region-model-manager.cc          | 14 +++++++++++++
 gcc/analyzer/region-model.cc                  | 21 +++++++++++++++++++
 gcc/analyzer/svalue.cc                        | 18 ++++++++--------
 gcc/analyzer/svalue.h                         | 21 ++++++++++++++-----
 .../c-c++-common/analyzer/divide-by-zero-1.c  | 19 +++++++++++++++++
 .../analyzer/divide-by-zero-pr124195-2.c      |  8 +++++++
 .../analyzer/divide-by-zero-pr124195-1.c      |  8 +++++++
 7 files changed, 95 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c
 create mode 100644 
gcc/testsuite/c-c++-common/analyzer/divide-by-zero-pr124195-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c

diff --git a/gcc/analyzer/region-model-manager.cc 
b/gcc/analyzer/region-model-manager.cc
index cfde061d65610..c7aaa38a0949c 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -791,6 +791,20 @@ region_model_manager::maybe_fold_binop (tree type, enum 
tree_code op,
            return get_or_create_cast (type, arg1);
        }
       break;
+
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case TRUNC_MOD_EXPR:
+    case CEIL_MOD_EXPR:
+    case FLOOR_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+    case RDIV_EXPR:
+    case EXACT_DIV_EXPR:
+      if (cst1 && zerop (cst1))
+       return get_or_create_unknown_svalue (type);
+      break;
     }
 
   /* For associative ops, fold "(X op CST_A) op CST_B)" to
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 2546f44795de7..a85c6958838ad 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1353,6 +1353,27 @@ region_model::get_gassign_result (const gassign *assign,
                }
          }
 
+       if (ctxt
+           && (op == TRUNC_DIV_EXPR
+               || op == CEIL_DIV_EXPR
+               || op == FLOOR_DIV_EXPR
+               || op == ROUND_DIV_EXPR
+               || op == TRUNC_MOD_EXPR
+               || op == CEIL_MOD_EXPR
+               || op == FLOOR_MOD_EXPR
+               || op == ROUND_MOD_EXPR
+               || op == RDIV_EXPR
+               || op == EXACT_DIV_EXPR))
+         {
+           if (const tree rhs2_cst = rhs2_sval->maybe_get_constant ())
+             if (zerop (rhs2_cst))
+               {
+                 /* Ideally we should issue a warning here;
+                    see PR analyzer/124217.  */
+                 return nullptr;
+               }
+         }
+
        const svalue *sval_binop
          = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
                                        rhs1_sval, rhs2_sval);
diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc
index be9daced7e8ed..74c2c0dbd4a37 100644
--- a/gcc/analyzer/svalue.cc
+++ b/gcc/analyzer/svalue.cc
@@ -895,12 +895,12 @@ type_can_have_value_range_p (tree type)
   return true;
 }
 
-/* Base implementation of svalue::maybe_get_value_range vfunc.
+/* Base implementation of svalue::maybe_get_value_range_1 vfunc.
    If there is a suitable underlying type, write a "varying" for it to OUT
    (for "any value of that type") and return true; otherwise return false.  */
 
 bool
-svalue::maybe_get_value_range (value_range &out) const
+svalue::maybe_get_value_range_1 (value_range &out) const
 {
   tree type = get_type ();
   if (!type_can_have_value_range_p (type))
@@ -1196,13 +1196,13 @@ constant_svalue::all_zeroes_p () const
 }
 
 
-/* Implementation of svalue::maybe_get_value_range for constant_svalue.
+/* Implementation of svalue::maybe_get_value_range_1 for constant_svalue.
    If there is a suitable underlying type, write the value_range for the
    single value of m_cst_expr to OUT and return true; otherwise return
    false.  */
 
 bool
-constant_svalue::maybe_get_value_range (value_range &out) const
+constant_svalue::maybe_get_value_range_1 (value_range &out) const
 {
   if (!type_can_have_value_range_p (get_type ()))
     return false;
@@ -1275,7 +1275,7 @@ unknown_svalue::maybe_fold_bits_within (tree type,
 }
 
 bool
-unknown_svalue::maybe_get_value_range (value_range &) const
+unknown_svalue::maybe_get_value_range_1 (value_range &) const
 {
   /* Don't attempt to participate in range ops.  */
   return false;
@@ -1572,10 +1572,10 @@ unaryop_svalue::maybe_fold_bits_within (tree type,
   return nullptr;
 }
 
-/* Implementation of svalue::maybe_get_value_range for unaryop_svalue.  */
+/* Implementation of svalue::maybe_get_value_range_1 for unaryop_svalue.  */
 
 bool
-unaryop_svalue::maybe_get_value_range (value_range &out) const
+unaryop_svalue::maybe_get_value_range_1 (value_range &out) const
 {
   tree type = get_type ();
   if (!type_can_have_value_range_p (type))
@@ -1716,10 +1716,10 @@ sub_svalue::sub_svalue (symbol::id_t id,
   gcc_assert (parent_svalue->can_have_associated_state_p ());
 }
 
-/* Implementation of svalue::maybe_get_value_range for binop_svalue.  */
+/* Implementation of svalue::maybe_get_value_range_1 for binop_svalue.  */
 
 bool
-binop_svalue::maybe_get_value_range (value_range &out) const
+binop_svalue::maybe_get_value_range_1 (value_range &out) const
 {
   tree type = get_type ();
   if (!type_can_have_value_range_p (type))
diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h
index bd3ffdd52d8f1..72b1542d730ed 100644
--- a/gcc/analyzer/svalue.h
+++ b/gcc/analyzer/svalue.h
@@ -192,7 +192,16 @@ public:
 
   /* If we can get a value_range for this svalue, write it to OUT
      and return true.  Otherwise return false.  */
-  virtual bool maybe_get_value_range (value_range &out) const;
+  bool
+  maybe_get_value_range (value_range &out) const
+  {
+    if (maybe_get_value_range_1 (out))
+      {
+       gcc_assert (!out.undefined_p ());
+       return true;
+      }
+    return false;
+  }
 
  protected:
   svalue (complexity c, symbol::id_t id, tree type)
@@ -207,6 +216,8 @@ public:
   virtual void
   add_dump_widget_children (text_art::tree_widget &,
                            const dump_widget_info &dwi) const = 0;
+  virtual bool
+  maybe_get_value_range_1 (value_range &out) const;
 
   tree m_type;
 };
@@ -370,7 +381,7 @@ public:
 
   bool all_zeroes_p () const final override;
 
-  bool maybe_get_value_range (value_range &out) const final override;
+  bool maybe_get_value_range_1 (value_range &out) const final override;
 
  private:
   tree m_cst_expr;
@@ -423,7 +434,7 @@ public:
                          const bit_range &subrange,
                          region_model_manager *mgr) const final override;
 
-  bool maybe_get_value_range (value_range &out) const final override;
+  bool maybe_get_value_range_1 (value_range &out) const final override;
 
   /* Unknown values are singletons per-type, so can't have state.  */
   bool can_have_associated_state_p () const final override { return false; }
@@ -771,7 +782,7 @@ public:
                          const bit_range &subrange,
                          region_model_manager *mgr) const final override;
 
-  bool maybe_get_value_range (value_range &out) const final override;
+  bool maybe_get_value_range_1 (value_range &out) const final override;
 
  private:
   enum tree_code m_op;
@@ -869,7 +880,7 @@ public:
   bool implicitly_live_p (const svalue_set *,
                          const region_model *) const final override;
 
-  bool maybe_get_value_range (value_range &out) const final override;
+  bool maybe_get_value_range_1 (value_range &out) const final override;
 
   enum tree_code get_op () const { return m_op; }
   const svalue *get_arg0 () const { return m_arg0; }
diff --git a/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c 
b/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c
new file mode 100644
index 0000000000000..5fbfe9c52ea47
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c
@@ -0,0 +1,19 @@
+#include "analyzer-decls.h"
+
+static int __attribute__((noipa))
+return_zero (void)
+{
+  return 0;
+}
+
+void
+test_div (int a)
+{
+  __analyzer_eval (a / return_zero () == 0); /* { dg-warning "UNKNOWN" } */
+}
+
+void
+test_mod (int a)
+{
+  __analyzer_eval (a % return_zero () == 0); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-pr124195-2.c 
b/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-pr124195-2.c
new file mode 100644
index 0000000000000..cac91e57d3c73
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-pr124195-2.c
@@ -0,0 +1,8 @@
+short s;
+
+int
+foo()
+{
+  s %= 0; /* { dg-warning "division by zero" } */
+  return s > 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c 
b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c
new file mode 100644
index 0000000000000..6be17ed61d448
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c
@@ -0,0 +1,8 @@
+short s;
+
+int
+foo()
+{
+  s %= (0, 0);
+  return s > 0;
+}
-- 
2.26.3

Reply via email to