analyzer: Add exit, and _exit replacement, to sm-signal.
authorMark Wielaard <mark@klomp.org>
Sun, 17 May 2020 21:50:41 +0000 (23:50 +0200)
committerMark Wielaard <mark@klomp.org>
Fri, 22 May 2020 19:02:34 +0000 (21:02 +0200)
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/ChangeLog
gcc/analyzer/sm-signal.cc
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/analyzer/signal-exit.c [new file with mode: 0644]

index 5cd736385aa75bed519ea3a6239c4bb43928b9ff..d2c440a08e45859a9db51d15f96b3a945c566946 100644 (file)
@@ -1,3 +1,10 @@
+2020-05-22  Mark Wielaard  <mark@klomp.org>
+
+       * 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".
+
 2020-04-28  David Malcolm  <dmalcolm@redhat.com>
 
        PR analyzer/94816
index 5935e229e77c19b7caded9a8929b44f2846a2dfb..c0020321071ece04900f21c09d563e048e858977 100644 (file)
@@ -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,20 @@ 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 (id_equal ("exit", DECL_NAME (m_unsafe_fndecl)))
+      return "_exit";
+
+    return NULL;
+  }
 };
 
 /* signal_state_machine's ctor.  */
@@ -259,6 +292,7 @@ get_async_signal_unsafe_fns ()
   // TODO: populate this list more fully
   static const char * const async_signal_unsafe_fns[] = {
     /* This array must be kept sorted.  */
+    "exit",
     "fprintf",
     "free",
     "malloc",
index 1dbdc389ab9d9b597dea6eb808a3a090ad37edaa..bedaf9aa73556153c52e1d6c0158ee86ef2778d5 100644 (file)
@@ -1,3 +1,7 @@
+2020-05-22  Mark Wielaard  <mark@klomp.org>
+
+       * gcc.dg/analyzer/signal-exit.c: New testcase.
+
 2020-05-22  Uroš Bizjak  <ubizjak@gmail.com>
 
        PR target/95255
diff --git a/gcc/testsuite/gcc.dg/analyzer/signal-exit.c b/gcc/testsuite/gcc.dg/analyzer/signal-exit.c
new file mode 100644 (file)
index 0000000..a567124
--- /dev/null
@@ -0,0 +1,23 @@
+/* Example of a bad call within a signal handler with replacement
+   alternative.  'handler' calls 'exit', and 'exit' is not allowed
+   from a signal handler.  But '_exit' is allowed.  */
+
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+static void handler(int signum)
+{
+  exit(1); /* { dg-warning "call to 'exit' from within signal handler" "warning" } */
+  /* { dg-message "note: '_exit' is a possible signal-safe alternative for 'exit'" "replacement note" { target *-*-* } .-1 } */
+}
+
+int main(int argc, const char *argv)
+{
+  signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+
+  body_of_program();
+
+  return 0;
+}