[RFC] analyzer: Add exit, and _exit replacement, to sm-signal.

Marek Polacek polacek@redhat.com
Mon May 18 23:30:58 GMT 2020


On Tue, May 19, 2020 at 01:19:13AM +0200, Mark Wielaard wrote:
> On Mon, May 18, 2020 at 05:24:58PM +0200, Mark Wielaard wrote:
> > On Mon, May 18, 2020 at 11:09:18AM -0400, David Malcolm wrote:
> > > Overall, I like it, but it looks like there's a problem with the
> > > location of the fix-it hint: it looks like it might be replacing the
> > > whole of the underlined section of the call, argument, parentheses and
> > > all, with "_exit", rather than just the "exit" token.  I wonder if the
> > > location of that token is still available in the middle-end by the time
> > > the analyzer runs.
> > > 
> > > What does -fdiagnostics-generate-patch emit?
> > 
> > --- bzip2.c
> > +++ bzip2.c
> > @@ -871,7 +871,7 @@
> >        terribly wrong. Trying to clean up might fail spectacularly. */
> >  
> >     if (opMode == OM_Z) setExit(3); else setExit(2);
> > -   exit(exitValue);
> > +   _exit;
> >  }
> > 
> > Hmmm, back to the drawing board it seems.
> 
> It seems it is impossible to untangle the gimple call. We do have the
> function decl, but this is associated with the declaration of the
> function. Since we know the call starts with a known symbol identifier
> (that is all on one line), it seems we should be able to truncate the
> source_range for the call location to just that prefix. But it is
> incredibly hard to manipulate locations. Neither seems it simple to
> get the actual text of the location or a range to sanity check what we
> are doing.
> 
> So I am afraid we have to drop the actual fixit. But we can still add
> the note itself. The diagnostic now looks as follows:
> 
> /opt/local/install/gcc/bin/gcc -g -O2 -fanalyzer -fanalyzer-fine-grained -c bzip2.c
> bzip2.c: In function ‘mySIGSEGVorSIGBUScatcher’:
> bzip2.c:874:4: warning: call to ‘exit’ from within signal handler [CWE-479] [-Wanalyzer-unsafe-call-within-signal-handler]
>   874 |    exit(exitValue);
>       |    ^~~~~~~~~~~~~~~
>   ‘main’: events 1-2
>     |
>     | 1784 | IntNative main ( IntNative argc, Char *argv[] )
>     |      |           ^~~~
>     |      |           |
>     |      |           (1) entry to ‘main’
>     |......
>     | 1816 |    signal (SIGSEGV, mySIGSEGVorSIGBUScatcher);
>     |      |    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>     |      |    |
>     |      |    (2) registering ‘mySIGSEGVorSIGBUScatcher’ as signal handler
>     |
>   event 3
>     |
>     |cc1:
>     | (3): later on, when the signal is delivered to the process
>     |
>     +--> ‘mySIGSEGVorSIGBUScatcher’: events 4-5
>            |
>            |  816 | void mySIGSEGVorSIGBUScatcher ( IntNative n )
>            |      |      ^~~~~~~~~~~~~~~~~~~~~~~~
>            |      |      |
>            |      |      (4) entry to ‘mySIGSEGVorSIGBUScatcher’
>            |......
>            |  874 |    exit(exitValue);
>            |      |    ~~~~~~~~~~~~~~~
>            |      |    |
>            |      |    (5) call to ‘exit’ from within signal handler
>            |
> bzip2.c:874:4: note: ‘_exit’ is a possible signal-safe alternative for ‘exit’
>   874 |    exit(exitValue);
>       |    ^~~~~~~~~~~~~~~
> 
> What do you think of the attached patch?
> 
> Thanks,
> 
> Mark

> From e47d9c6898b0db7f56cff03096b3fccaeb801efa Mon Sep 17 00:00:00 2001
> From: Mark Wielaard <mark@klomp.org>
> Date: Sun, 17 May 2020 23:50:41 +0200
> Subject: [PATCH] analyzer: Add exit, and _exit replacement, to sm-signal.
> 
> Warn about using exit in signal handler and suggest _exit as alternative.
> 
> gcc/analyzer/ChangeLog:
> 	* sm-signal.cc(signal_unsafe_call::emit): Possibly add
> 	gcc_rich_location note for replacement.
> 	(signal_unsafe_call::get_replacement_fn): New private function.
> 	(get_async_signal_unsafe_fns): Add "exit".
> 
> gcc/testsuite/ChangeLog:
> 	* gcc.dg/analyzer/signal-exit.c: New testcase.
> ---
>  gcc/analyzer/sm-signal.cc                   | 43 +++++++++++++++++++--
>  gcc/testsuite/gcc.dg/analyzer/signal-exit.c | 23 +++++++++++
>  2 files changed, 62 insertions(+), 4 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/analyzer/signal-exit.c
> 
> diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
> index 5935e229e77c..379f58004219 100644
> --- a/gcc/analyzer/sm-signal.cc
> +++ b/gcc/analyzer/sm-signal.cc
> @@ -123,13 +123,32 @@ public:
>  
>    bool emit (rich_location *rich_loc) FINAL OVERRIDE
>    {
> +    auto_diagnostic_group d;
>      diagnostic_metadata m;
>      /* CWE-479: Signal Handler Use of a Non-reentrant Function.  */
>      m.add_cwe (479);
> -    return warning_meta (rich_loc, m,
> -			 OPT_Wanalyzer_unsafe_call_within_signal_handler,
> -			 "call to %qD from within signal handler",
> -			 m_unsafe_fndecl);
> +    if (warning_meta (rich_loc, m,
> +		      OPT_Wanalyzer_unsafe_call_within_signal_handler,
> +		      "call to %qD from within signal handler",
> +		      m_unsafe_fndecl))
> +      {
> +	/* If we know a possible alternative function, add a note
> +	   suggesting the replacement.  */
> +	if (const char *replacement = get_replacement_fn ())
> +	  {
> +	    location_t note_loc = gimple_location (m_unsafe_call);
> +	    /* It would be nice to add a fixit, but the gimple call
> +	       location covers the whole call expression.  It isn't
> +	       currently possible to cut this down to just the call
> +	       symbol.  So the fixit would replace too much.
> +	       note_rich_loc.add_fixit_replace (replacement); */
> +	    inform (note_loc,
> +		    "%qs is a possible signal-safe alternative for %qD",
> +		    replacement, m_unsafe_fndecl);
> +	  }
> +	return true;
> +      }
> +    return false;
>    }
>  
>    label_text describe_state_change (const evdesc::state_change &change)
> @@ -156,6 +175,21 @@ private:
>    const signal_state_machine &m_sm;
>    const gcall *m_unsafe_call;
>    tree m_unsafe_fndecl;
> +
> +  /* Returns a replacement function as text if it exists.  Currently
> +     only "exit" has a signal-safe replacement "_exit", which does
> +     slightly less, but can be used in a signal handler.  */
> +  const char *
> +  get_replacement_fn ()
> +  {
> +    gcc_assert (m_unsafe_fndecl && DECL_P (m_unsafe_fndecl));
> +
> +    if (strcmp ("exit",
> +		IDENTIFIER_POINTER (DECL_NAME (m_unsafe_fndecl))) == 0)

Instead of strcmp, you should be able to use id_equal here, making this a bit
simpler.

Marek



More information about the Gcc-patches mailing list