From: David Malcolm Date: Tue, 24 Feb 2026 23:47:35 +0000 (-0500) Subject: analyzer: new warning: -Wanalyzer-div-by-zero (PR analyzer/124217) X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=5b0937f037e3b341cd906db07cedf016ba78dd3b;p=thirdparty%2Fgcc.git analyzer: new warning: -Wanalyzer-div-by-zero (PR analyzer/124217) 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 --- diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt index 389f4ea946a..992ef9f8a08 100644 --- a/gcc/analyzer/analyzer.opt +++ b/gcc/analyzer/analyzer.opt @@ -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. diff --git a/gcc/analyzer/analyzer.opt.urls b/gcc/analyzer/analyzer.opt.urls index db56a7209e9..833d0d76c76 100644 --- a/gcc/analyzer/analyzer.opt.urls +++ b/gcc/analyzer/analyzer.opt.urls @@ -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) diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 02b00bc03c9..e4bafebbaa4 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -855,6 +855,46 @@ private: const region *m_base_reg_b; }; +class div_by_zero_diagnostic +: public pending_diagnostic_subclass +{ +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 (assign)); + ctxt->terminate_path (); + } return nullptr; } } diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index daf2f3ccd8f..824da5df793 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -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. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 81b14ab7cf0..9604d6ce3d9 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -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 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 index 5fbfe9c52ea..0d0b8e01157 100644 --- a/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c +++ b/gcc/testsuite/c-c++-common/analyzer/divide-by-zero-1.c @@ -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\\\]" } */ } 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 index cac91e57d3c..dc2edc9958e 100644 --- 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 @@ -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; } diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c index ce77d125477..35699e0bb1e 100644 --- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c @@ -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 index 00000000000..3aaee568bc1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-float.c @@ -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\\\]" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-ice-pr124433.c b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-ice-pr124433.c index 9b52ac8cc5b..0853939e5b1 100644 --- a/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-ice-pr124433.c +++ b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-ice-pr124433.c @@ -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); } 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 index 6be17ed61d4..d11cfeb4f97 100644 --- a/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/divide-by-zero-pr124195-1.c @@ -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; }