analyzer: fix setjmp-detection and support sigsetjmp
authorDavid Malcolm <dmalcolm@redhat.com>
Thu, 23 Jan 2020 17:19:20 +0000 (12:19 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Mon, 27 Jan 2020 15:10:48 +0000 (10:10 -0500)
This patch removes the hack in is_setjmp_call_p of looking for
"setjmp" and "_setjmp", replacing it with some logic adapted from
special_function_p in calls.c, ignoring up to 2 leading underscores from
the fndecl's name when checking for a function by name.

It also requires that such functions are "extern" and at file scope
for them to be matched.

The patch also generalizes the setjmp/longjmp handling in the analyzer
to also work with sigsetjmp/siglongjmp.  Doing so requires generalizing
some hardcoded functions in diagnostics (which were hardcoded to avoid
user-facing messages referring to "_setjmp", which is an implementation
detail) - the patch adds a new function, get_user_facing_name for this,
for use on calls that matched is_named_call_p and
is_specical_named_call_p.

gcc/analyzer/ChangeLog:
* analyzer.cc  (is_named_call_p): Check that fndecl is "extern"
and at file scope.  Potentially disregard prefix _ or __ in
fndecl's name.  Bail if the identifier is NULL.
(is_setjmp_call_p): Expect a gcall rather than plain gimple.
Remove special-case check for leading prefix, and also check for
sigsetjmp.
(is_longjmp_call_p): Also check for siglongjmp.
(get_user_facing_name): New function.
* analyzer.h (is_setjmp_call_p): Expect a gcall rather than plain
gimple.
(get_user_facing_name): New decl.
* checker-path.cc (setjmp_event::get_desc): Use
get_user_facing_name to avoid hardcoding the function name.
(rewind_event::rewind_event): Add rewind_info param, using it to
initialize new m_rewind_info field, and strengthen the assertion.
(rewind_from_longjmp_event::get_desc): Use get_user_facing_name to
avoid hardcoding the function name.
(rewind_to_setjmp_event::get_desc): Likewise.
* checker-path.h (setjmp_event::setjmp_event): Add setjmp_call
param and use it to initialize...
(setjmp_event::m_setjmp_call): New field.
(rewind_event::rewind_event): Add rewind_info param.
(rewind_event::m_rewind_info): New protected field.
(rewind_from_longjmp_event::rewind_from_longjmp_event): Add
rewind_info param.
(class rewind_to_setjmp_event): Move rewind_info field to parent
class.
* diagnostic-manager.cc (diagnostic_manager::add_events_for_eedge):
Update setjmp-handling for is_setjmp_call_p requiring a gcall;
pass the call to the new setjmp_event.
* engine.cc (exploded_node::on_stmt): Update for is_setjmp_call_p
requiring a gcall.
(stale_jmp_buf::emit): Use get_user_facing_name to avoid
hardcoding the function names.
(exploded_node::on_longjmp): Pass the longjmp_call when
constructing rewind_info.
(rewind_info_t::add_events_to_path): Pass the rewind_info_t to the
rewind_from_longjmp_event's ctor.
* exploded-graph.h (rewind_info_t::rewind_info_t): Add
longjmp_call param.
(rewind_info_t::get_longjmp_call): New.
(rewind_info_t::m_longjmp_call): New.
* region-model.cc (region_model::on_setjmp): Update comment to
indicate this is also for sigsetjmp.
* region-model.h (struct setjmp_record): Likewise.
(class setjmp_svalue): Likewise.

gcc/testsuite/ChangeLog:
* gcc.dg/analyzer/sigsetjmp-5.c: New test.
* gcc.dg/analyzer/sigsetjmp-6.c: New test.

13 files changed:
gcc/analyzer/ChangeLog
gcc/analyzer/analyzer.cc
gcc/analyzer/analyzer.h
gcc/analyzer/checker-path.cc
gcc/analyzer/checker-path.h
gcc/analyzer/diagnostic-manager.cc
gcc/analyzer/engine.cc
gcc/analyzer/exploded-graph.h
gcc/analyzer/region-model.cc
gcc/analyzer/region-model.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c [new file with mode: 0644]

index 359ea14a7487b940fc0d11a87ea321dc5e67c072..345d40f3faf83fec7bd55148f371ec79dd0233c4 100644 (file)
@@ -1,3 +1,52 @@
+2020-01-27  David Malcolm  <dmalcolm@redhat.com>
+
+       * analyzer.cc  (is_named_call_p): Check that fndecl is "extern"
+       and at file scope.  Potentially disregard prefix _ or __ in
+       fndecl's name.  Bail if the identifier is NULL.
+       (is_setjmp_call_p): Expect a gcall rather than plain gimple.
+       Remove special-case check for leading prefix, and also check for
+       sigsetjmp.
+       (is_longjmp_call_p): Also check for siglongjmp.
+       (get_user_facing_name): New function.
+       * analyzer.h (is_setjmp_call_p): Expect a gcall rather than plain
+       gimple.
+       (get_user_facing_name): New decl.
+       * checker-path.cc (setjmp_event::get_desc): Use
+       get_user_facing_name to avoid hardcoding the function name.
+       (rewind_event::rewind_event): Add rewind_info param, using it to
+       initialize new m_rewind_info field, and strengthen the assertion.
+       (rewind_from_longjmp_event::get_desc): Use get_user_facing_name to
+       avoid hardcoding the function name.
+       (rewind_to_setjmp_event::get_desc): Likewise.
+       * checker-path.h (setjmp_event::setjmp_event): Add setjmp_call
+       param and use it to initialize...
+       (setjmp_event::m_setjmp_call): New field.
+       (rewind_event::rewind_event): Add rewind_info param.
+       (rewind_event::m_rewind_info): New protected field.
+       (rewind_from_longjmp_event::rewind_from_longjmp_event): Add
+       rewind_info param.
+       (class rewind_to_setjmp_event): Move rewind_info field to parent
+       class.
+       * diagnostic-manager.cc (diagnostic_manager::add_events_for_eedge):
+       Update setjmp-handling for is_setjmp_call_p requiring a gcall;
+       pass the call to the new setjmp_event.
+       * engine.cc (exploded_node::on_stmt): Update for is_setjmp_call_p
+       requiring a gcall.
+       (stale_jmp_buf::emit): Use get_user_facing_name to avoid
+       hardcoding the function names.
+       (exploded_node::on_longjmp): Pass the longjmp_call when
+       constructing rewind_info.
+       (rewind_info_t::add_events_to_path): Pass the rewind_info_t to the
+       rewind_from_longjmp_event's ctor.
+       * exploded-graph.h (rewind_info_t::rewind_info_t): Add
+       longjmp_call param.
+       (rewind_info_t::get_longjmp_call): New.
+       (rewind_info_t::m_longjmp_call): New.
+       * region-model.cc (region_model::on_setjmp): Update comment to
+       indicate this is also for sigsetjmp.
+       * region-model.h (struct setjmp_record): Likewise.
+       (class setjmp_svalue): Likewise.
+
 2020-01-27  David Malcolm  <dmalcolm@redhat.com>
 
        PR analyzer/93276
index 3884788ee9e88ba15971e57ed23ae187c530f1cb..1b5e4c9ecf89ebbdc5f4a896602a60ec816de023 100644 (file)
@@ -54,7 +54,10 @@ is_special_named_call_p (const gcall *call, const char *funcname,
   return is_named_call_p (fndecl, funcname, call, num_args);
 }
 
-/* Helper function for checkers.  Does FNDECL have the given FUNCNAME?  */
+/* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
+   that has the given FUNCNAME?
+
+   Compare with special_function_p in calls.c.  */
 
 bool
 is_named_call_p (tree fndecl, const char *funcname)
@@ -62,11 +65,38 @@ is_named_call_p (tree fndecl, const char *funcname)
   gcc_assert (fndecl);
   gcc_assert (funcname);
 
-  return 0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname);
+  /* Exclude functions not at the file scope, or not `extern',
+     since they are not the magic functions we would otherwise
+     think they are.  */
+  if (!((DECL_CONTEXT (fndecl) == NULL_TREE
+        || TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL)
+       && TREE_PUBLIC (fndecl)))
+    return false;
+
+  tree identifier = DECL_NAME (fndecl);
+  if (identifier == NULL)
+    return false;
+
+  const char *name = IDENTIFIER_POINTER (identifier);
+  const char *tname = name;
+
+  /* Potentially disregard prefix _ or __ in FNDECL's name, but not if
+     FUNCNAME itself has leading underscores (e.g. when looking for
+     "__analyzer_eval").  */
+  if (funcname[0] != '_' && name[0] == '_')
+    {
+      if (name[1] == '_')
+       tname += 2;
+      else
+       tname += 1;
+    }
+
+  return 0 == strcmp (tname, funcname);
 }
 
-/* Helper function for checkers.  Does FNDECL have the given FUNCNAME, and
-   does CALL have the given number of arguments?  */
+/* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
+   that has the given FUNCNAME, and does CALL have the given number of
+   arguments?  */
 
 bool
 is_named_call_p (tree fndecl, const char *funcname,
@@ -84,32 +114,57 @@ is_named_call_p (tree fndecl, const char *funcname,
   return true;
 }
 
-/* Return true if stmt is a setjmp call.  */
+/* Return true if stmt is a setjmp or sigsetjmp call.  */
 
 bool
-is_setjmp_call_p (const gimple *stmt)
+is_setjmp_call_p (const gcall *call)
 {
-  /* TODO: is there a less hacky way to check for "setjmp"?  */
-  if (const gcall *call = dyn_cast <const gcall *> (stmt))
-    if (is_special_named_call_p (call, "setjmp", 1)
-       || is_special_named_call_p (call, "_setjmp", 1))
-      return true;
+  if (is_special_named_call_p (call, "setjmp", 1)
+      || is_special_named_call_p (call, "sigsetjmp", 2))
+    return true;
 
   return false;
 }
 
-/* Return true if stmt is a longjmp call.  */
+/* Return true if stmt is a longjmp or siglongjmp call.  */
 
 bool
 is_longjmp_call_p (const gcall *call)
 {
-  /* TODO: is there a less hacky way to check for "longjmp"?  */
-  if (is_special_named_call_p (call, "longjmp", 2))
+  if (is_special_named_call_p (call, "longjmp", 2)
+      || is_special_named_call_p (call, "siglongjmp", 2))
     return true;
 
   return false;
 }
 
+/* For a CALL that matched is_special_named_call_p or is_named_call_p for
+   some name, return a name for the called function suitable for use in
+   diagnostics (stripping the leading underscores).  */
+
+const char *
+get_user_facing_name (const gcall *call)
+{
+  tree fndecl = gimple_call_fndecl (call);
+  gcc_assert (fndecl);
+
+  tree identifier = DECL_NAME (fndecl);
+  gcc_assert (identifier);
+
+  const char *name = IDENTIFIER_POINTER (identifier);
+
+  /* Strip prefix _ or __ in FNDECL's name.  */
+  if (name[0] == '_')
+    {
+      if (name[1] == '_')
+       return name + 2;
+      else
+       return name + 1;
+    }
+
+  return name;
+}
+
 /* Generate a label_text instance by formatting FMT, using a
    temporary clone of the global_dc's printer (thus using its
    formatting callbacks).
index 9746c9e0123585520a1a906a17125e7fb6cd0a9b..f1602e38b36743a5ddcb698c49017860920a7e95 100644 (file)
@@ -78,9 +78,11 @@ extern bool is_special_named_call_p (const gcall *call, const char *funcname,
 extern bool is_named_call_p (tree fndecl, const char *funcname);
 extern bool is_named_call_p (tree fndecl, const char *funcname,
                             const gcall *call, unsigned int num_args);
-extern bool is_setjmp_call_p (const gimple *stmt);
+extern bool is_setjmp_call_p (const gcall *call);
 extern bool is_longjmp_call_p (const gcall *call);
 
+extern const char *get_user_facing_name (const gcall *call);
+
 extern void register_analyzer_pass ();
 
 extern label_text make_label_text (bool can_colorize, const char *fmt, ...);
index f7455ba12450cc53d91a704707b1726981e984e9..7f6cdf599cf85a5379f24b3ee2992b36409dae83 100644 (file)
@@ -709,7 +709,7 @@ setjmp_event::get_desc (bool can_colorize) const
 {
   return make_label_text (can_colorize,
                          "%qs called here",
-                         "setjmp");
+                         get_user_facing_name (m_setjmp_call));
 }
 
 /* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event.
@@ -748,11 +748,13 @@ rewind_event::get_setjmp_caller () const
 
 rewind_event::rewind_event (const exploded_edge *eedge,
                            enum event_kind kind,
-                           location_t loc, tree fndecl, int depth)
+                           location_t loc, tree fndecl, int depth,
+                           const rewind_info_t *rewind_info)
 : checker_event (kind, loc, fndecl, depth),
+  m_rewind_info (rewind_info),
   m_eedge (eedge)
 {
-  gcc_assert (m_eedge->m_custom_info); // a rewind_info_t
+  gcc_assert (m_eedge->m_custom_info == m_rewind_info);
 }
 
 /* class rewind_from_longjmp_event : public rewind_event.  */
@@ -763,7 +765,8 @@ rewind_event::rewind_event (const exploded_edge *eedge,
 label_text
 rewind_from_longjmp_event::get_desc (bool can_colorize) const
 {
-  const char *src_name = "longjmp";
+  const char *src_name
+    = get_user_facing_name (m_rewind_info->get_longjmp_call ());
 
   if (get_longjmp_caller () == get_setjmp_caller ())
     /* Special-case: purely intraprocedural rewind.  */
@@ -786,7 +789,8 @@ rewind_from_longjmp_event::get_desc (bool can_colorize) const
 label_text
 rewind_to_setjmp_event::get_desc (bool can_colorize) const
 {
-  const char *dst_name = "setjmp";
+  const char *dst_name
+    = get_user_facing_name (m_rewind_info->get_setjmp_call ());
 
   /* If we can, identify the ID of the setjmp_event.  */
   if (m_original_setjmp_event_id.known_p ())
index cceffe079fa146817f5798575f6fcb72fcbf3a85..30cb43c13ba1f0859e1204417a77dd8571b2522a 100644 (file)
@@ -329,15 +329,15 @@ public:
   bool is_return_p () const FINAL OVERRIDE;
 };
 
-/* A concrete event subclass for a setjmp call.  */
+/* A concrete event subclass for a setjmp or sigsetjmp call.  */
 
 class setjmp_event : public checker_event
 {
 public:
   setjmp_event (location_t loc, const exploded_node *enode,
-               tree fndecl, int depth)
+               tree fndecl, int depth, const gcall *setjmp_call)
   : checker_event (EK_SETJMP, loc, fndecl, depth),
-    m_enode (enode)
+    m_enode (enode), m_setjmp_call (setjmp_call)
   {
   }
 
@@ -349,9 +349,12 @@ public:
 
 private:
   const exploded_node *m_enode;
+  const gcall *m_setjmp_call;
 };
 
-/* An abstract event subclass for rewinding from a longjmp to a setjmp.
+/* An abstract event subclass for rewinding from a longjmp to a setjmp
+   (or siglongjmp to sigsetjmp).
+
    Base class for two from/to subclasses, showing the two halves of the
    rewind.  */
 
@@ -365,21 +368,25 @@ public:
  protected:
   rewind_event (const exploded_edge *eedge,
                enum event_kind kind,
-               location_t loc, tree fndecl, int depth);
+               location_t loc, tree fndecl, int depth,
+               const rewind_info_t *rewind_info);
+  const rewind_info_t *m_rewind_info;
 
  private:
   const exploded_edge *m_eedge;
 };
 
 /* A concrete event subclass for rewinding from a longjmp to a setjmp,
-   showing the longjmp.  */
+   showing the longjmp (or siglongjmp).  */
 
 class rewind_from_longjmp_event : public rewind_event
 {
 public:
   rewind_from_longjmp_event (const exploded_edge *eedge,
-                            location_t loc, tree fndecl, int depth)
-  : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth)
+                            location_t loc, tree fndecl, int depth,
+                            const rewind_info_t *rewind_info)
+  : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth,
+                 rewind_info)
   {
   }
 
@@ -387,7 +394,7 @@ public:
 };
 
 /* A concrete event subclass for rewinding from a longjmp to a setjmp,
-   showing the setjmp.  */
+   showing the setjmp (or sigsetjmp).  */
 
 class rewind_to_setjmp_event : public rewind_event
 {
@@ -395,8 +402,8 @@ public:
   rewind_to_setjmp_event (const exploded_edge *eedge,
                          location_t loc, tree fndecl, int depth,
                          const rewind_info_t *rewind_info)
-  : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth),
-    m_rewind_info (rewind_info)
+  : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth,
+                 rewind_info)
   {
   }
 
@@ -408,7 +415,6 @@ public:
 
 private:
   diagnostic_event_id_t m_original_setjmp_event_id;
-  const rewind_info_t *m_rewind_info;
 };
 
 /* Concrete subclass of checker_event for use at the end of a path:
index 02bc4a61e60af2d74f66dc98cdb3a9262a12c249..eb1fa05533ebe8fb559e2a1333d07726d1b87fc8 100644 (file)
@@ -818,12 +818,14 @@ diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge,
     case PK_BEFORE_STMT:
       {
        const gimple *stmt = dst_point.get_stmt ();
-       if (is_setjmp_call_p (stmt))
+       const gcall *call = dyn_cast <const gcall *> (stmt);
+       if (call && is_setjmp_call_p (call))
          emission_path->add_event
            (new setjmp_event (stmt->location,
                               dst_node,
                               dst_point.get_fndecl (),
-                              dst_stack_depth));
+                              dst_stack_depth,
+                              call));
        else
          emission_path->add_event
            (new statement_event (stmt,
index a2587a33a66e002ef0b40d9edebd5314326aae02..b39058f9c10311a19e7421144f2c0214b69c8e47 100644 (file)
@@ -1001,7 +1001,7 @@ exploded_node::on_stmt (exploded_graph &eg,
        {
          /* This is handled elsewhere.  */
        }
-      else if (is_setjmp_call_p (stmt))
+      else if (is_setjmp_call_p (call))
        state->m_region_model->on_setjmp (call, this, &ctxt);
       else if (is_longjmp_call_p (call))
        {
@@ -1126,7 +1126,8 @@ public:
     return warning_at
       (richloc, OPT_Wanalyzer_stale_setjmp_buffer,
        "%qs called after enclosing function of %qs has returned",
-       "longjmp", "setjmp");
+       get_user_facing_name (m_longjmp_call),
+       get_user_facing_name (m_setjmp_call));
   }
 
   const char *get_kind () const FINAL OVERRIDE
@@ -1143,10 +1144,10 @@ private:
   const gcall *m_longjmp_call;
 };
 
-/* Handle LONGJMP_CALL, a call to "longjmp".
+/* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.
 
-   Attempt to locate where "setjmp" was called on the jmp_buf and build an
-   exploded_node and exploded_edge to it representing a rewind to that frame,
+   Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build
+   an exploded_node and exploded_edge to it representing a rewind to that frame,
    handling the various kinds of failure that can occur.  */
 
 void
@@ -1174,9 +1175,9 @@ exploded_node::on_longjmp (exploded_graph &eg,
 
   const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record ();
 
-  /* Build a custom enode and eedge for rewinding from the longjmp
-     call back to the setjmp.  */
-  rewind_info_t rewind_info (tmp_setjmp_record);
+  /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp
+     call back to the setjmp/sigsetjmp.  */
+  rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call);
 
   const gcall *setjmp_call = rewind_info.get_setjmp_call ();
   const program_point &setjmp_point = rewind_info.get_setjmp_point ();
@@ -1217,7 +1218,7 @@ exploded_node::on_longjmp (exploded_graph &eg,
       exploded_edge *eedge
        = eg.add_edge (const_cast<exploded_node *> (this), next, NULL,
                       change,
-                      new rewind_info_t (tmp_setjmp_record));
+                      new rewind_info_t (tmp_setjmp_record, longjmp_call));
 
       /* For any diagnostics that were queued here (such as leaks) we want
         the checker_path to show the rewinding events after the "final event"
@@ -1369,7 +1370,7 @@ rewind_info_t::add_events_to_path (checker_path *emission_path,
     (new rewind_from_longjmp_event
      (&eedge, src_point.get_supernode ()->get_end_location (),
       src_point.get_fndecl (),
-      src_stack_depth));
+      src_stack_depth, this));
   emission_path->add_event
     (new rewind_to_setjmp_event
      (&eedge, get_setjmp_call ()->location,
index 3d1445c87ad0937b5be3f6bf99221bb87865d040..a3e758ed7513817a6fd937aaf676695981efb98b 100644 (file)
@@ -302,13 +302,15 @@ private:
 };
 
 /* Extra data for an exploded_edge that represents a rewind from a
-   longjmp to a setjmp.  */
+   longjmp to a setjmp (or from a siglongjmp to a sigsetjmp).  */
 
 class rewind_info_t : public exploded_edge::custom_info_t
 {
 public:
-  rewind_info_t (const setjmp_record &setjmp_record)
-  : m_setjmp_record (setjmp_record)
+  rewind_info_t (const setjmp_record &setjmp_record,
+                const gcall *longjmp_call)
+  : m_setjmp_record (setjmp_record),
+    m_longjmp_call (longjmp_call)
   {}
 
   void print (pretty_printer *pp) FINAL OVERRIDE
@@ -339,6 +341,11 @@ public:
     return m_setjmp_record.m_setjmp_call;
   }
 
+  const gcall *get_longjmp_call () const
+  {
+    return m_longjmp_call;
+  }
+
   const exploded_node *get_enode_origin () const
   {
     return m_setjmp_record.m_enode;
@@ -346,6 +353,7 @@ public:
 
 private:
   setjmp_record m_setjmp_record;
+  const gcall *m_longjmp_call;
 };
 
 /* Statistics about aspects of an exploded_graph.  */
index 25a22f8fc65b45b31f1f03848d7a8604438620ff..985f1bd56ac55045f6b19b9d5cf5bea1403222a5 100644 (file)
@@ -4480,11 +4480,11 @@ region_model::on_return (const greturn *return_stmt, region_model_context *ctxt)
     set_value (get_lvalue (lhs, ctxt), get_rvalue (rhs, ctxt), ctxt);
 }
 
-/* Update this model for a call and return of "setjmp" at CALL within ENODE,
-   using CTXT to report any diagnostics.
+/* Update this model for a call and return of setjmp/sigsetjmp at CALL within
+   ENODE, using CTXT to report any diagnostics.
 
-   This is for the initial direct invocation of setjmp (which returns 0),
-   as opposed to any second return due to longjmp.  */
+   This is for the initial direct invocation of setjmp/sigsetjmp (which returns
+   0), as opposed to any second return due to longjmp/sigsetjmp.  */
 
 void
 region_model::on_setjmp (const gcall *call, const exploded_node *enode,
index f7fb7b0b6d0a63a2851ba9874a8fceb8e4831604..70e3eb4c716fe3383d9c60a2d4fc40a592d2a72b 100644 (file)
@@ -718,8 +718,8 @@ is_a_helper <poisoned_svalue *>::test (svalue *sval)
 
 namespace ana {
 
-/* A bundle of information recording a setjmp call, corresponding roughly
-   to a jmp_buf.  */
+/* A bundle of information recording a setjmp/sigsetjmp call, corresponding
+   roughly to a jmp_buf.  */
 
 struct setjmp_record
 {
@@ -739,8 +739,9 @@ struct setjmp_record
   const gcall *m_setjmp_call;
 };
 
-/* Concrete subclass of svalue representing setjmp buffers, so that
-   longjmp can potentially "return" to an entirely different function.  */
+/* Concrete subclass of svalue representing buffers for setjmp/sigsetjmp,
+   so that longjmp/siglongjmp can potentially "return" to an entirely
+   different function.  */
 
 class setjmp_svalue : public svalue
 {
index bd76fa78e91c5204d1389e5beb0573e9a36d284b..e8928777fd9ec1f5eeba8a8564568a479467a633 100644 (file)
@@ -1,3 +1,8 @@
+2020-01-27  David Malcolm  <dmalcolm@redhat.com>
+
+       * gcc.dg/analyzer/sigsetjmp-5.c: New test.
+       * gcc.dg/analyzer/sigsetjmp-6.c: New test.
+
 2020-01-27  Richard Biener  <rguenther@suse.de>
 
        PR testsuite/91171
diff --git a/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c
new file mode 100644 (file)
index 0000000..68afe9d
--- /dev/null
@@ -0,0 +1,19 @@
+#include <setjmp.h>
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  sigsetjmp (env, 0); /* { dg-message "'sigsetjmp' called here" } */
+}
+
+void outer (void)
+{
+  int i;
+
+  inner ();
+
+  siglongjmp (env, 42); /* { dg-warning "'siglongjmp' called after enclosing function of 'sigsetjmp' has returned" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c
new file mode 100644 (file)
index 0000000..fcd9d0b
--- /dev/null
@@ -0,0 +1,35 @@
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+extern int foo (int) __attribute__ ((__pure__));
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  void *ptr = malloc (1024); /* { dg-message "allocated here" }  */
+
+  siglongjmp (env, 1); /* { dg-warning "leak of 'ptr'" "warning" } */
+  /* { dg-message "rewinding from 'siglongjmp' in 'inner'" " event: rewind from" { target *-*-* } .-1 } */
+
+  free (ptr);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = sigsetjmp(env, 0); /* { dg-message "'sigsetjmp' called here" "event: sigsetjmp call" } */
+  /* { dg-message "to 'sigsetjmp' in 'outer'" "event: rewind to"  { target *-*-* } .-1 } */
+
+  if (i == 0)
+    {
+      foo (1);
+      inner ();
+    }
+
+  foo (3);
+}