public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/c++-modules] analyzer: fix setjmp-detection and support sigsetjmp
@ 2020-01-31 17:42 Nathan Sidwell
  0 siblings, 0 replies; only message in thread
From: Nathan Sidwell @ 2020-01-31 17:42 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:342e14ffa30e9163a1a75e0a4fb21b6883d58dbe

commit 342e14ffa30e9163a1a75e0a4fb21b6883d58dbe
Author: David Malcolm <dmalcolm@redhat.com>
Date:   Thu Jan 23 12:19:20 2020 -0500

    analyzer: fix setjmp-detection and support sigsetjmp
    
    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.

Diff:
---
 gcc/analyzer/ChangeLog                      | 49 +++++++++++++++++
 gcc/analyzer/analyzer.cc                    | 83 ++++++++++++++++++++++++-----
 gcc/analyzer/analyzer.h                     |  4 +-
 gcc/analyzer/checker-path.cc                | 14 +++--
 gcc/analyzer/checker-path.h                 | 30 ++++++-----
 gcc/analyzer/diagnostic-manager.cc          |  6 ++-
 gcc/analyzer/engine.cc                      | 21 ++++----
 gcc/analyzer/exploded-graph.h               | 14 +++--
 gcc/analyzer/region-model.cc                |  8 +--
 gcc/analyzer/region-model.h                 |  9 ++--
 gcc/testsuite/ChangeLog                     |  5 ++
 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c | 19 +++++++
 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c | 35 ++++++++++++
 13 files changed, 242 insertions(+), 55 deletions(-)

diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog
index 359ea14..345d40f 100644
--- a/gcc/analyzer/ChangeLog
+++ b/gcc/analyzer/ChangeLog
@@ -1,5 +1,54 @@
 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
 	* analyzer.h (PUSH_IGNORE_WFORMAT, POP_IGNORE_WFORMAT): Guard these
 	macros with GCC_VERSION >= 4006, making them no-op otherwise.
diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 3884788..1b5e4c9 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -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).
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 9746c9e..f1602e3 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -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, ...);
diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc
index f7455ba..7f6cdf5 100644
--- a/gcc/analyzer/checker-path.cc
+++ b/gcc/analyzer/checker-path.cc
@@ -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 ())
diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h
index cceffe0..30cb43c 100644
--- a/gcc/analyzer/checker-path.h
+++ b/gcc/analyzer/checker-path.h
@@ -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:
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 02bc4a6..eb1fa05 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -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,
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index a2587a3..b39058f 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -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,
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 3d1445c..a3e758e 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -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.  */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 25a22f8..985f1bd 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -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,
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index f7fb7b0..70e3eb4 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -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
 {
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index bd76fa7..e892877 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..68afe9d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c
@@ -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
index 0000000..fcd9d0b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c
@@ -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);
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-01-31 17:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-31 17:42 [gcc/devel/c++-modules] analyzer: fix setjmp-detection and support sigsetjmp Nathan Sidwell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).