]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
analyzer: new warning: -Wanalyzer-div-by-zero (PR analyzer/124217)
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 24 Feb 2026 23:47:35 +0000 (18:47 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 28 Apr 2026 23:11:30 +0000 (19:11 -0400)
gcc/analyzer/ChangeLog:
PR analyzer/124217
* analyzer.opt (Wanalyzer-div-by-zero): New.
* analyzer.opt.urls: Regenerate.
* region-model.cc (class div_by_zero_diagnostic): New.
(region_model::get_gassign_result): Add warning for division by
zero if ctxt is non-null.  Bail out on such cases even if ctxt
is null.
* svalue.cc (type_can_have_value_range_p): Also handle frange.

gcc/ChangeLog:
PR analyzer/124217
* doc/invoke.texi: Add -Wanalyzer-div-by-zero.

gcc/testsuite/ChangeLog:
PR analyzer/124217
* c-c++-common/analyzer/divide-by-zero-1.c: Update to expect
-Wanalyzer-div-by-zero.
* c-c++-common/analyzer/divide-by-zero-pr124195-2.c: Likewise.
* gcc.dg/analyzer/data-model-1.c (test_21): Split out division by
zero cases into...
(test_21_division_by_zero): ...this, and...
(test_21_modulus_by_zero): ...this, updating these to expect
-Wanalyzer-div-by-zero warnings.
* gcc.dg/analyzer/divide-by-zero-float.c: New test.
* gcc.dg/analyzer/divide-by-zero-ice-pr124433.c: Update to expect
-Wanalyzer-div-by-zero.
* gcc.dg/analyzer/divide-by-zero-pr124195-1.c: Likewise.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
gcc/analyzer/analyzer.opt
gcc/analyzer/analyzer.opt.urls
gcc/analyzer/region-model.cc
gcc/analyzer/svalue.cc
gcc/doc/invoke.texi
gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c
gcc/testsuite/c-c++-common/analyzer/divide-by-zero-pr124195-2.c
gcc/testsuite/gcc.dg/analyzer/data-model-1.c
gcc/testsuite/gcc.dg/analyzer/divide-by-zero-float.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/divide-by-zero-ice-pr124433.c
gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c

index 389f4ea946ac5ca4c82bb7abda8ba60ce04f7373..992ef9f8a08fa9e142c6630fb1880cd15959e6af 100644 (file)
@@ -78,6 +78,10 @@ Wanalyzer-deref-before-check
 Common Var(warn_analyzer_deref_before_check) Init(1) Warning
 Warn about code paths in which a pointer is checked for NULL after it has already been dereferenced.
 
+Wanalyzer-div-by-zero
+Common Var(warn_analyzer_div_by_zero) Init(1) Warning
+Warn about code paths which attempt integer division by zero.
+
 Wanalyzer-double-fclose
 Common Var(warn_analyzer_double_fclose) Init(1) Warning
 Warn about code paths in which a stdio FILE can be closed more than once.
index db56a7209e93c6b94acf4b7f0830ba8a54404f8b..833d0d76c76b18787bc9f063d94f224121b36fe7 100644 (file)
@@ -6,6 +6,9 @@ UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-allocation-size)
 Wanalyzer-deref-before-check
 UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-deref-before-check)
 
+Wanalyzer-div-by-zero
+UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-div-by-zero)
+
 Wanalyzer-double-fclose
 UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-double-fclose)
 
index 02b00bc03c96fae669290207f71e36342df7b96f..e4bafebbaa49b955eb4492cdc579b4978daeeb9e 100644 (file)
@@ -855,6 +855,46 @@ private:
   const region *m_base_reg_b;
 };
 
+class div_by_zero_diagnostic
+: public pending_diagnostic_subclass<div_by_zero_diagnostic>
+{
+public:
+  div_by_zero_diagnostic (const gassign *assign)
+  : m_assign (assign)
+  {}
+
+  const char *get_kind () const final override
+  {
+    return "div_by_zero_diagnostic";
+  }
+
+  bool operator== (const div_by_zero_diagnostic &other) const
+  {
+    return m_assign == other.m_assign;
+  }
+
+  int get_controlling_option () const final override
+  {
+    return OPT_Wanalyzer_div_by_zero;
+  }
+
+  bool emit (diagnostic_emission_context &ctxt) final override
+  {
+    return ctxt.warn ("division by zero");
+  }
+
+  bool
+  describe_final_event (pretty_printer &pp,
+                       const evdesc::final_event &) final override
+  {
+    pp_printf (&pp, "division by zero");
+    return true;
+  }
+
+private:
+  const gassign *m_assign;
+};
+
 /* Check the pointer subtraction SVAL_A - SVAL_B at ASSIGN and add
    a warning to CTXT if they're not within the same base region.  */
 
@@ -1073,24 +1113,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 (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)
          {
            value_range rhs_vr;
            if (rhs2_sval->maybe_get_value_range (rhs_vr))
              if (rhs_vr.zero_p ())
                {
-                 /* Ideally we should issue a warning here;
-                    see PR analyzer/124217.  */
+                 if (ctxt)
+                   {
+                     ctxt->warn
+                       (std::make_unique<div_by_zero_diagnostic> (assign));
+                     ctxt->terminate_path ();
+                   }
                  return nullptr;
                }
          }
index daf2f3ccd8fa08e55f3bc000212c6369840d1a76..824da5df793f85580ca277c8b215d3a56d650074 100644 (file)
@@ -891,9 +891,11 @@ type_can_have_value_range_p (tree type)
 {
   if (!type)
     return false;
-  if (!irange::supports_p (type))
-    return false;
-  return true;
+  if (irange::supports_p (type))
+    return true;
+  if (frange::supports_p (type))
+    return true;
+  return false;
 }
 
 /* Base implementation of svalue::maybe_get_value_range_1 vfunc.
index 81b14ab7cf071a824cad6bdb47933911fcff6434..9604d6ce3d9173c00c9905c8f4999b0c148590e0 100644 (file)
@@ -11555,6 +11555,7 @@ Enabling this option effectively enables the following warnings:
 @gccoptlist{
 -Wanalyzer-allocation-size
 -Wanalyzer-deref-before-check
+-Wanalyzer-div-by-zero
 -Wanalyzer-double-fclose
 -Wanalyzer-double-free
 -Wanalyzer-exposure-through-output-file
@@ -11646,6 +11647,18 @@ multiple of @code{sizeof (*pointer)}.
 
 See @uref{https://cwe.mitre.org/data/definitions/131.html, CWE-131: Incorrect Calculation of Buffer Size}.
 
+@opindex Wanalyzer-div-by-zero
+@opindex Wno-analyzer-div-by-zero
+@item -Wno-analyzer-div-by-zero
+This warning requires @option{-fanalyzer}, which enables it;
+to disable it, use @option{-Wno-analyzer-div-by-zero}.
+
+This diagnostic warns for paths through the code which attempt
+integer division by zero.  It is analogous to @option{-Wdiv-by-zero}, but
+implemented in a different way.
+
+See @uref{https://cwe.mitre.org/data/definitions/369.html,  CWE-369: Divide By Zero}.
+
 @opindex Wanalyzer-deref-before-check
 @opindex Wno-analyzer-deref-before-check
 @item -Wno-analyzer-deref-before-check
index 5fbfe9c52ea47a53d85840a430ce6101ec9d53cb..0d0b8e01157c02de5e468c41da582ec0b2bf1ce8 100644 (file)
@@ -9,11 +9,11 @@ return_zero (void)
 void
 test_div (int a)
 {
-  __analyzer_eval (a / return_zero () == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (a / return_zero () == 0); /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
 }
 
 void
 test_mod (int a)
 {
-  __analyzer_eval (a % return_zero () == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (a % return_zero () == 0); /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
 }
index cac91e57d3c73ee3144ca41bdc52c04f25b13eaa..dc2edc9958ef1bcf8440564b00631b7648dc48a3 100644 (file)
@@ -3,6 +3,7 @@ short s;
 int
 foo()
 {
-  s %= 0; /* { dg-warning "division by zero" } */
+  s %= 0; /* { dg-warning "division by zero \\\[-Wdiv-by-zero\\\]" } */
+  /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" "" { target *-*-* } .-1 } */
   return s > 0;
 }
index ce77d1254772a003d0e876e32bb8ff07affd0014..35699e0bb1e8af6785baba04ebbf900b1a8a91b5 100644 (file)
@@ -421,11 +421,6 @@ void test_21 (void)
   __analyzer_eval (i / j == 1); /* { dg-warning "TRUE" } */
   __analyzer_eval (i % j == 2); /* { dg-warning "TRUE" } */
 
-  /* Division by zero.  */
-  // TODO: should we warn for this?
-  __analyzer_eval (i / zero); /* { dg-warning "UNKNOWN" } */
-  __analyzer_eval (i % zero); /* { dg-warning "UNKNOWN" } */
-
   __analyzer_eval ((i & 1) == (5 & 1)); /* { dg-warning "TRUE" } */
   __analyzer_eval ((i & j) == (5 & 3)); /* { dg-warning "TRUE" } */
   __analyzer_eval ((i | 1) == (5 | 1)); /* { dg-warning "TRUE" } */
@@ -449,6 +444,28 @@ void test_21 (void)
   __analyzer_eval (+i == +5); /* { dg-warning "TRUE" } */
 }
 
+void test_21_division_by_zero (void)
+{
+  int i, zero;
+  int *pi = &i;
+  int *pzero = &zero;
+  *pi = 5;
+  *pzero = 0;
+
+  __analyzer_eval (i / zero); /* { dg-warning "Wanalyzer-div-by-zero" } */
+}
+
+void test_21_modulus_by_zero (void)
+{
+  int i, zero;
+  int *pi = &i;
+  int *pzero = &zero;
+  *pi = 5;
+  *pzero = 0;
+
+  __analyzer_eval (i % zero); /* { dg-warning "Wanalyzer-div-by-zero" } */
+}
+
 void test_22 (int i, int j)
 {
   __analyzer_eval (i + j == i + j); /* { dg-warning "TRUE" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-float.c b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-float.c
new file mode 100644 (file)
index 0000000..3aaee56
--- /dev/null
@@ -0,0 +1,23 @@
+float
+test_1 ()
+{
+  return 42.f / 0.f; /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
+}
+
+static float __attribute__((noinline))
+get_zero ()
+{
+  return 0.f;
+}
+
+float
+test_2 ()
+{
+  return 42.f / get_zero (); /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
+}
+
+float
+test_3 (float x)
+{
+  return x / get_zero (); /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
+}
index 9b52ac8cc5bf4b350b516120837d194d868fcd10..0853939e5b1a5dea0126c820f87f9fdee968921c 100644 (file)
@@ -4,6 +4,6 @@ int c;
 void
 foo()
 {
-  do c %= (5ull << 40) & m;
+  do c %= (5ull << 40) & m; /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
   while (c);
 }
index 6be17ed61d448bbc11975cc2f746f861f96febfe..d11cfeb4f97b7fb463146cab316756917525bdb8 100644 (file)
@@ -3,6 +3,6 @@ short s;
 int
 foo()
 {
-  s %= (0, 0);
+  s %= (0, 0); /* { dg-warning "division by zero \\\[-Wanalyzer-div-by-zero\\\]" } */
   return s > 0;
 }