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