On Mon, 2026-01-26 at 19:19 -0500, David Malcolm wrote:
> As of r16-264-g7a39e0ca0652ff, -fanalyzer assumes that a call to an
> external function not marked with attribute "nothrow" could throw an
> exception, if -fexceptions is enabled.
> 
> PR analyzer/122623 notes that testing -fanalyzer with GCC 16 on
> Fedora
> packages turned up some new leak warnings due to -fexceptions being
> passed to all C code (for interoperability with C++), due to C
> headers
> typically not having their entrypoints being marked with "nothrow".
> Some of these are false positives.  Others are arguably true
> positives,
> such as the case in the above report, but highly surprising to
> end-users, and of dubious value.
> 
> I don't have data on the scale of the problem, but I am worried that
> the C++ exception support added in GCC 16 could cause a big
> regression
> in analyzer signal:noise when compiling C code with distro build
> flags.
> 
> To provide a workaround for distro mass analysis runs, this patch
> adds a
> new option: -fanalyzer-assume-nothrow, which when enabled assumes
> that
> external functions do not throw exceptions.  This may be something of
> a
> blunt hammer, but may be useful to distros to add to build flags for
> C,
> to allow the combination "-fexceptions -fanalyzer-assume-nothrow" so
> that the generated code supports exception-handling, but the analyzer
> ignores it.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> Successful run of analyzer integration tests on x86_64-pc-linux-gnu
> without the option set; I plan to try again with the option set.

I ran the analyzer integration tests with the option enabled, and it
seemed to fix some false positives there.

> Given the above, I'm thinking of applying this to trunk for GCC 16.

Given that, and that I got feedback off-list that this would indeed be
useful for using -fanalyzer for mass-scans of Fedora SRPMs, I've gone
ahead and pushed the patch to trunk for GCC 16, as r16-7080-
gf1318516f07453.


Dave


> 
> Thoughts?
> 
> Thanks
> Dave
> 
> 
> gcc/analyzer/ChangeLog:
>       PR analyzer/122623
>       * analyzer.opt (fanalyzer-assume-nothrow): New.
>       * analyzer.opt.urls: Regenerate.
>       * region-model.cc (can_throw_p): Bail out if the user
> specified
>       -fanalyzer-assume-nothrow.
> 
> gcc/ChangeLog:
>       PR analyzer/122623
>       * doc/invoke.texi (-fanalyzer-assume-nothrow): New option.
> 
> gcc/testsuite/ChangeLog:
>       PR analyzer/122623
>       * gcc.dg/analyzer/fexceptions-1.c: New test.
>       * gcc.dg/analyzer/fexceptions-2.c: New test.
> 
> Signed-off-by: David Malcolm <[email protected]>
> ---
>  gcc/analyzer/analyzer.opt                     |  4 +++
>  gcc/analyzer/analyzer.opt.urls                |  3 +++
>  gcc/analyzer/region-model.cc                  |  7 +++++
>  gcc/doc/invoke.texi                           | 16 ++++++++++++
>  gcc/testsuite/gcc.dg/analyzer/fexceptions-1.c | 23 ++++++++++++++++
>  gcc/testsuite/gcc.dg/analyzer/fexceptions-2.c | 26
> +++++++++++++++++++
>  6 files changed, 79 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/fexceptions-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/fexceptions-2.c
> 
> diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
> index cceee1015015..3c5dd0849c60 100644
> --- a/gcc/analyzer/analyzer.opt
> +++ b/gcc/analyzer/analyzer.opt
> @@ -278,6 +278,10 @@ Wanalyzer-too-complex
>  Common Var(warn_analyzer_too_complex) Init(0) Warning
>  Warn if the code is too complicated for the analyzer to fully
> explore.
>  
> +fanalyzer-assume-nothrow
> +Common Var(flag_analyzer_assume_nothrow) Init(0)
> +Assume that no function calls can throw exceptions.
> +
>  fanalyzer-checker=
>  Common Joined RejectNegative Var(flag_analyzer_checker)
>  Restrict the analyzer to run just the named checker.
> diff --git a/gcc/analyzer/analyzer.opt.urls
> b/gcc/analyzer/analyzer.opt.urls
> index 0ad321e77f57..1a698f9c6d9a 100644
> --- a/gcc/analyzer/analyzer.opt.urls
> +++ b/gcc/analyzer/analyzer.opt.urls
> @@ -156,6 +156,9 @@ UrlSuffix(gcc/Static-Analyzer-Options.html#index-
> Wanalyzer-symbol-too-complex)
>  Wanalyzer-too-complex
>  UrlSuffix(gcc/Static-Analyzer-Options.html#index-Wanalyzer-too-
> complex)
>  
> +fanalyzer-assume-nothrow
> +UrlSuffix(gcc/Static-Analyzer-Options.html#index-fanalyzer-assume-
> nothrow)
> +
>  fanalyzer-checker=
>  UrlSuffix(gcc/Static-Analyzer-Options.html#index-fanalyzer-checker)
>  
> diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-
> model.cc
> index c6b22706c7b1..1efb19b07761 100644
> --- a/gcc/analyzer/region-model.cc
> +++ b/gcc/analyzer/region-model.cc
> @@ -2198,6 +2198,13 @@ can_throw_p (const gcall &call, tree fndecl)
>    if (!flag_exceptions)
>      return false;
>  
> +  /* Compatibility flag to allow the user to assume external
> functions
> +     never throw exceptions.  This may be useful when using the
> analyzer
> +     on C code that is compiled with -fexceptions, but for which the
> headers
> +     haven't yet had "nothrow" attributes systematically added.  */
> +  if (flag_analyzer_assume_nothrow)
> +    return false;
> +
>    if (gimple_call_nothrow_p (&call))
>      return false;
>  
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index d690305a890a..cad9e993140b 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -483,6 +483,7 @@ Objective-C and Objective-C++ Dialects}.
>  @item Static Analyzer Options
>  @gccoptlist{
>  -fanalyzer
> +-fanalyzer-assume-nothrow
>  -fanalyzer-call-summaries
>  -fanalyzer-checker=@var{name}
>  -fno-analyzer-feasibility
> @@ -12430,6 +12431,21 @@ The following options control the analyzer.
>  
>  @table @gcctabopt
>  
> +@opindex fanalyzer-assume-nothrow
> +@opindex fno-analyzer-assume-nothrow
> +@item -fanalyzer-assume-nothrow
> +By default, if @option{-fexceptions} is enabled, the analyzer will
> assume
> +that a call to any function without attribute @code{nothrow} could
> throw
> +an exception.  This can help detect execution paths that leak due to
> +exceptions bypassing clean-up code, but could lead to false
> positives when
> +using headers where the author has not added the @code{nothrow}
> attribute.
> +
> +If @option{-fanalyzer-assume-nothrow} is enabled, then the analyzer
> will
> +assume that external functions do not throw exceptions.  This may be
> useful
> +for reducing ``noise'' from the analyzer when enabling
> +@option{-fexceptions} on C code, but could hide true issues if an
> +exception could be raised by something the C code calls.
> +
>  @opindex fanalyzer-call-summaries
>  @opindex fno-analyzer-call-summaries
>  @item -fanalyzer-call-summaries
> diff --git a/gcc/testsuite/gcc.dg/analyzer/fexceptions-1.c
> b/gcc/testsuite/gcc.dg/analyzer/fexceptions-1.c
> new file mode 100644
> index 000000000000..052265d7a7d5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/analyzer/fexceptions-1.c
> @@ -0,0 +1,23 @@
> +/* { dg-additional-options "-fexceptions" } */
> +
> +extern void do_something ();
> +extern void do_something_nothrow () __attribute__ ((nothrow));
> +
> +void test ()
> +{
> +  void *p = __builtin_malloc (1024);
> +
> +  do_something (); /* { dg-warning "leak of 'p'" } */
> +  /* { dg-message "if 'do_something' throws an exception\.\.\."
> "exception event" { target *-*-* } .-1 } */
> +
> +  __builtin_free (p);
> +}
> +
> +void test_nothrow ()
> +{
> +  void *p = __builtin_malloc (1024);
> +
> +  do_something_nothrow (); /* { dg-bogus "leak of 'p'" } */
> +
> +  __builtin_free (p);
> +}
> diff --git a/gcc/testsuite/gcc.dg/analyzer/fexceptions-2.c
> b/gcc/testsuite/gcc.dg/analyzer/fexceptions-2.c
> new file mode 100644
> index 000000000000..78d463c01c78
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/analyzer/fexceptions-2.c
> @@ -0,0 +1,26 @@
> +/* Verify that -fanalyzer-assume-nothrow suppresses warnings about
> +   exceptions being thrown in called function, even those not
> +   marked with "nothrow".  */
> +
> +/* { dg-additional-options "-fexceptions -fanalyzer-assume-nothrow"
> } */
> +
> +extern void do_something ();
> +extern void do_something_nothrow () __attribute__ ((nothrow));
> +
> +void test ()
> +{
> +  void *p = __builtin_malloc (1024);
> +
> +  do_something (); /* { dg-bogus "leak of 'p'" } */
> +
> +  __builtin_free (p);
> +}
> +
> +void test_nothrow ()
> +{
> +  void *p = __builtin_malloc (1024);
> +
> +  do_something_nothrow (); /* { dg-bogus "leak of 'p'" } */
> +
> +  __builtin_free (p);
> +}

Reply via email to