https://gcc.gnu.org/g:761e0d3d785794064477e7b68310dd5c6ae1003c
commit r16-7671-g761e0d3d785794064477e7b68310dd5c6ae1003c Author: David Malcolm <[email protected]> Date: Tue Feb 24 17:16:58 2026 -0500 analyzer: fix ICE on division by zero [PR124195] 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]> Diff: --- 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 ++++++++ .../gcc.dg/analyzer/divide-by-zero-pr124195-1.c | 8 ++++++++ 7 files changed, 95 insertions(+), 14 deletions(-) diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index cfde061d6561..c7aaa38a0949 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 2546f44795de..a85c6958838a 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 be9daced7e8e..74c2c0dbd4a3 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 bd3ffdd52d8f..72b1542d730e 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 000000000000..5fbfe9c52ea4 --- /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 000000000000..cac91e57d3c7 --- /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 000000000000..6be17ed61d44 --- /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; +}
