analyzer: fix setjmp-detection and support sigsetjmp

Message ID 20200123221648.22050-1-dmalcolm@redhat.com
State New
Headers show
Series
  • analyzer: fix setjmp-detection and support sigsetjmp
Related show

Commit Message

David Malcolm Jan. 23, 2020, 10:16 p.m.
On Wed, 2020-01-22 at 20:40 +0100, Jakub Jelinek wrote:
> On Wed, Jan 22, 2020 at 02:35:13PM -0500, David Malcolm wrote:

> > PR analyzer/93316 reports various testsuite failures where I

> > accidentally relied on properties of x86_64-pc-linux-gnu.

> > 

> > The following patch fixes them on sparc-sun-solaris2.11 (gcc211 in

> > the

> > GCC compile farm), and, I hope, the other configurations showing

> > failures.

> > 

> > There may still be other failures for pattern-test-2.c, which I'm

> > tracking separately as PR analyzer/93291.

> > 

> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu;

> > tested on stage 1 on sparc-sun-solaris2.11.

> > 

> > gcc/analyzer/ChangeLog:

> > 	PR analyzer/93316

> > 	* analyzer.cc (is_setjmp_call_p): Check for "setjmp" as well as

> > 	"_setjmp".

> 

> Please see calls.c (special_function_p), you should treat certainly

> also sigsetjmp as a setjmp call, and similarly to special_function_p,

> skip over _ or __ prefixes before the setjmp or sigsetjmp name.

> Similarly for longjmp/siglongjmp.

> 

> 	Jakub


Thanks.

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.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for master?

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.
---
 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/gcc.dg/analyzer/sigsetjmp-5.c | 19 +++++
 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c | 35 +++++++++
 11 files changed, 188 insertions(+), 55 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c

-- 
2.21.0

Comments

David Malcolm Jan. 27, 2020, 3:15 p.m. | #1
On Thu, 2020-01-23 at 17:16 -0500, David Malcolm wrote:
> On Wed, 2020-01-22 at 20:40 +0100, Jakub Jelinek wrote:

> > On Wed, Jan 22, 2020 at 02:35:13PM -0500, David Malcolm wrote:

> > > PR analyzer/93316 reports various testsuite failures where I

> > > accidentally relied on properties of x86_64-pc-linux-gnu.

> > > 

> > > The following patch fixes them on sparc-sun-solaris2.11 (gcc211

> > > in

> > > the

> > > GCC compile farm), and, I hope, the other configurations showing

> > > failures.

> > > 

> > > There may still be other failures for pattern-test-2.c, which I'm

> > > tracking separately as PR analyzer/93291.

> > > 

> > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu;

> > > tested on stage 1 on sparc-sun-solaris2.11.

> > > 

> > > gcc/analyzer/ChangeLog:

> > > 	PR analyzer/93316

> > > 	* analyzer.cc (is_setjmp_call_p): Check for "setjmp" as well as

> > > 	"_setjmp".

> > 

> > Please see calls.c (special_function_p), you should treat certainly

> > also sigsetjmp as a setjmp call, and similarly to

> > special_function_p,

> > skip over _ or __ prefixes before the setjmp or sigsetjmp name.

> > Similarly for longjmp/siglongjmp.

> > 

> > 	Jakub

> 

> Thanks.

> 

> 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.

> 

> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

> 

> OK for master?


I've gone ahead and committed this, based on Jeff's blanket approval
here:
  https://gcc.gnu.org/ml/gcc-patches/2020-01/msg01689.html

I'm working on a followup which would refactor it to share more code
with calls.c (which would need review, as it would touch calls.c).

Dave

Patch

diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 3884788ee9e..1b5e4c9ecf8 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 e84e6958cec..90ed241c553 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 f7455ba1245..7f6cdf599cf 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 cceffe079fa..30cb43c13ba 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 02bc4a61e60..eb1fa05533e 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 737ea1dd6e4..1fdedf49224 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 3d1445c87ad..a3e758ed751 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 25a22f8fc65..985f1bd56ac 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 f7fb7b0b6d0..70e3eb4c716 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/gcc.dg/analyzer/sigsetjmp-5.c b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c
new file mode 100644
index 00000000000..68afe9d1c97
--- /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 00000000000..fcd9d0bbb47
--- /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);
+}