From 6a0ff82ae0414e209c675e964aab0f9f51e1f836 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 20 Jun 2019 16:19:27 -0400
Subject: [PATCH 35/47] FIXME: precision-of-wording for calls and returns

---
 gcc/analyzer/hardcoded-state-machine-malloc.cc     |  41 ++++--
 gcc/analyzer/hardcoded-state-machine.h             | 122 ++++++++++++----
 gcc/analyzer/impl-2.cc                             |  15 +-
 gcc/analyzer/impl-3.cc                             |  10 +-
 gcc/analyzer/impl-4.cc                             | 157 ++++++++++++++++-----
 gcc/analyzer/supergraph.cc                         |  61 ++++++--
 gcc/analyzer/supergraph.h                          |  44 +++++-
 .../gcc.dg/analyzer/analyzer-verbosity-0.c         |   4 +-
 .../gcc.dg/analyzer/analyzer-verbosity-1.c         |   4 +-
 .../gcc.dg/analyzer/analyzer-verbosity-2.c         |   4 +-
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c      |   2 +-
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c      |   2 +-
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c      |  30 ++++
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c       |   2 +-
 .../gcc.dg/analyzer/malloc-ipa-8-double-free.c     |  50 ++++---
 .../gcc.dg/analyzer/malloc-ipa-8-unchecked.c       |   2 +-
 16 files changed, 417 insertions(+), 133 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c

diff --git a/gcc/analyzer/hardcoded-state-machine-malloc.cc b/gcc/analyzer/hardcoded-state-machine-malloc.cc
index beba3c5..ba987ab 100644
--- a/gcc/analyzer/hardcoded-state-machine-malloc.cc
+++ b/gcc/analyzer/hardcoded-state-machine-malloc.cc
@@ -90,7 +90,7 @@ public:
 #endif
   }
 
-  label_text describe_critical_state_change (const state_change &change)
+  label_text describe (const evdesc::state_change &change)
     FINAL OVERRIDE
   {
     if (change.m_new_state == m_sm.m_free)
@@ -101,8 +101,18 @@ public:
     return label_text ();
   }
 
+  label_text describe (const evdesc::call_with_state &info)
+    FINAL OVERRIDE
+  {
+    if (info.m_state == m_sm.m_free)
+      return formatted_print
+	("passing freed pointer %qE in call to %qE from %qE",
+	 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
+    return label_text ();
+  }
+
   /* FIXME.  */
-  label_text describe_final_event (const final_event &) FINAL OVERRIDE
+  label_text describe (const evdesc::final_event &) FINAL OVERRIDE
   {
     return formatted_print ("second %qs here", "free");
   }
@@ -131,7 +141,7 @@ public:
   : malloc_diagnostic (sm, arg)
   {}
 
-  label_text describe_critical_state_change (const state_change &change)
+  label_text describe (const evdesc::state_change &change)
     FINAL OVERRIDE
   {
     if (change.m_old_state == m_sm.m_start
@@ -139,6 +149,15 @@ public:
       return label_text::borrow ("this call could return NULL");
     return label_text ();
   }
+
+  label_text describe (const evdesc::return_of_state &info)
+    FINAL OVERRIDE
+  {
+    if (info.m_state == m_sm.m_unchecked)
+      return formatted_print ("possible return of NULL to %qE from %qE",
+			      info.m_caller_fndecl, info.m_callee_fndecl);
+    return label_text ();
+  }
 };
 
 /* Concrete subclass for describing dereference of a possible NULL
@@ -163,7 +182,7 @@ public:
   }
 
   /* FIXME.  */
-  label_text describe_final_event (const final_event &ev) FINAL OVERRIDE
+  label_text describe (const evdesc::final_event &ev) FINAL OVERRIDE
   {
     return formatted_print ("%qE could be NULL", ev.m_expr);
   }
@@ -223,7 +242,7 @@ public:
   }
 
   /* FIXME.  */
-  label_text describe_final_event (const final_event &ev) FINAL OVERRIDE
+  label_text describe (const evdesc::final_event &ev) FINAL OVERRIDE
   {
     return formatted_print ("argument %u (%qE) could be NULL"
 			    " where non-null expected",
@@ -292,7 +311,7 @@ public:
   }
 
   /* FIXME.  */
-  label_text describe_final_event (const final_event &ev) FINAL OVERRIDE
+  label_text describe (const evdesc::final_event &ev) FINAL OVERRIDE
   {
     return formatted_print ("argument %u (%qE) NULL"
 			    " where non-null expected",
@@ -322,7 +341,7 @@ public:
 		"use after free of %qE", m_arg);
   }
 
-  label_text describe_critical_state_change (const state_change &change)
+  label_text describe (const evdesc::state_change &change)
     FINAL OVERRIDE
   {
     if (change.m_new_state == m_sm.m_free)
@@ -347,7 +366,7 @@ public:
 		"leak of %qE", m_arg);
   }
 
-  label_text describe_critical_state_change (const state_change &change)
+  label_text describe (const evdesc::state_change &change)
     FINAL OVERRIDE
   {
     if (change.m_new_state == m_sm.m_unchecked)
@@ -356,7 +375,7 @@ public:
   }
 
   /* FIXME.  */
-  label_text describe_final_event (const final_event &ev) FINAL OVERRIDE
+  label_text describe (const evdesc::final_event &ev) FINAL OVERRIDE
   {
     return formatted_print ("%qE leaks here", ev.m_expr);
   }
@@ -403,7 +422,7 @@ public:
       }
   }
 
-  label_text describe_critical_state_change (const state_change &change)
+  label_text describe (const evdesc::state_change &change)
     FINAL OVERRIDE
   {
     /* Attempt to reconstruct what kind of pointer it is.
@@ -426,7 +445,7 @@ public:
   }
 
   /* FIXME.  */
-  label_text describe_final_event (const final_event &) FINAL OVERRIDE
+  label_text describe (const evdesc::final_event &) FINAL OVERRIDE
   {
     return formatted_print ("call to %qs here", "free");
   }
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index a23151e..9577f09 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -133,10 +133,24 @@ class event_id_t
   int m_index; // zero-based
 };
 
-/* A bundle of information for describing a critical state change
-   used for generating more precise messages for events within a
+/* Various bundles of information used for generating more precise
+   messages for events within a diagnostic_path, for passing to
+   "describe" vfuncs of pending_diagnostic.  */
+
+namespace evdesc {
+
+/* Information for describing a critical state change within the
    diagnostic_path.
-   Used by pending_diagnostic::describe_critical_state_change.  */
+
+   For example, a double-free diagnostic might use the descriptions:
+   - "first 'free' happens here"
+   - "second 'free' happens here"
+   for the pertinent events, whereas a use-after-free might use the
+   descriptions:
+   - "freed here"
+   - "use after free here"
+   Note how in both cases the first event is a "free": the best
+   description to use depends on the diagnostic.  */
 
 struct state_change
 {
@@ -154,9 +168,61 @@ struct state_change
   event_id_t m_event_id;
 };
 
-/* A bundle of information for describing the final event within a
-   diagnostic_path.
-   Used by pending_diagnostic::describe_final_event.  */
+/* Information for describing an interprocedural call carrying
+   critial state for the diagnostic, from caller to callee.
+
+   For example a double-free diagnostic might use:
+     "passing freed pointer 'ptr' in call to 'deallocator' from 'test'"
+   to make it clearer how the unchecked value moves from caller
+   to callee.  */
+
+struct call_with_state
+{
+  call_with_state (tree caller_fndecl, tree callee_fndecl,
+		   tree expr, state_machine::state_t state)
+  : m_caller_fndecl (caller_fndecl),
+    m_callee_fndecl (callee_fndecl),
+    m_expr (expr),
+    m_state (state)
+  {
+  }
+
+  tree m_caller_fndecl;
+  tree m_callee_fndecl;
+  tree m_expr;
+  state_machine::state_t m_state;
+};
+
+/* Information for describing an interprocedural return within the
+   diagnostic_path that carries critial state for the diagnostic,
+   from callee back to caller.
+
+   For example, a deref-of-unchecked-malloc diagnostic might use:
+   - "returning possibly-NULL pointer to 'make_obj' from 'allocator'"
+   to make it clearer how the unchecked value moves from callee
+   back to caller.  */
+
+struct return_of_state
+{
+  return_of_state (tree caller_fndecl, tree callee_fndecl,
+		   state_machine::state_t state)
+  : m_caller_fndecl (caller_fndecl),
+    m_callee_fndecl (callee_fndecl),
+    m_state (state)
+  {
+  }
+
+  tree m_caller_fndecl;
+  tree m_callee_fndecl;
+  state_machine::state_t m_state;
+};
+
+/* Information for describing the final event within a diagnostic_path.
+
+   For example a double-free diagnostic might use:
+     "second 'free' here"
+   and a use-after-free might use
+     "use after 'free' here".  */
 
 struct final_event
 {
@@ -168,6 +234,8 @@ struct final_event
   state_machine::state_t m_state;
 };
 
+} /* end of namespace evdesc */
+
 /* FIXME:
    Diagnostics are emitted using va_list, which can't be portably stored
    for later use, so we have to make it be a vfunc.  */
@@ -199,36 +267,40 @@ class pending_diagnostic
      below for a convenience subclass for implementing this.  */
   virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0;
 
-  /* For greatest precision-of-wording, give the pending diagnostic
-     a chance to describe critical state changes in terms of the
-     diagnostic.
+  /* For greatest precision-of-wording, the various following overloads of
+     "describe" are virtual functions that give the pending diagnostic a
+     way to describe events in a diagnostic_path in terms that make sense
+     for that diagnostic.
 
-     For example, a double-free diagnostic might use the descriptions:
-     - "first 'free' happens here"
-     - "second 'free' happens here"
-     for the pertinent events, whereas a use-after-free might use the
-     descriptions:
-     - "freed here"
-     - "use after free here"
-     Note how in both cases the first event is a "free": the best
-     description to use depends on the diagnostic.
+     In each case, return a non-NULL label_text to give the event a custom
+     description; NULL otherwise (falling back on a more generic
+     description).
 
-     Return a non-NULL label_text to give the state change a custom
-     description; NULL otherwise.  */
+     Refer to the various "evdesc" struct definitions above for more
+     information on the meanings of each event kind.  */
 
-  virtual label_text describe_critical_state_change (const state_change &)
+  virtual label_text describe (const evdesc::state_change &)
   {
     /* Default no-op implementation.  */
     return label_text ();
   }
-
-  /* A vfunc for describing the final event in a diagnostic_path, such
-     as "second 'free' here", or "use after 'free' here".  */
-  virtual label_text describe_final_event (const final_event &)
+  virtual label_text describe (const evdesc::call_with_state &)
   {
     /* Default no-op implementation.  */
     return label_text ();
   }
+  virtual label_text describe (const evdesc::return_of_state &)
+  {
+    /* Default no-op implementation.  */
+    return label_text ();
+  }
+  virtual label_text describe (const evdesc::final_event &)
+  {
+    /* Default no-op implementation.  */
+    return label_text ();
+  }
+
+  /* End of precision-of-wording vfuncs.  */
 
   static label_text formatted_print (const char *fmt, ...)
     ATTRIBUTE_GCC_DIAG(1,2);
diff --git a/gcc/analyzer/impl-2.cc b/gcc/analyzer/impl-2.cc
index 5a581c7..c62fa44 100644
--- a/gcc/analyzer/impl-2.cc
+++ b/gcc/analyzer/impl-2.cc
@@ -698,8 +698,8 @@ public:
 	if (m_pending_diagnostic)
 	  {
 	    label_text custom_desc
-	      = m_pending_diagnostic->describe_critical_state_change
-		  (state_change (m_var, m_from, m_to, m_emission_id));
+	      = m_pending_diagnostic->describe
+		  (evdesc::state_change (m_var, m_from, m_to, m_emission_id));
 	    if (custom_desc.m_buffer)
 	      {
 		if (flag_analyzer_verbose_state_changes)
@@ -3070,7 +3070,7 @@ engine::expand_path (const checker_path &in_path,
 		  if (maybe_parm && TREE_CODE (maybe_parm) == PARM_DECL)
 		    {
 		      // TODO: what about ecs?
-		      tree arg = call_edge->get_arg_for_parm (maybe_parm);
+		      tree arg = call_edge->get_arg_for_parm (maybe_parm, NULL);
 		      if (arg)
 			{
 			  log ("event %i: switching var of interest from"
@@ -3103,7 +3103,8 @@ engine::expand_path (const checker_path &in_path,
 	    /* Attempt to convert "var" to the callee scope.  */
 	    call_superedge *call_edge
 	      = summary_event->get_edge_for_call (&m_sg);
-	    tree callee_var = call_edge->map_expr_from_caller_to_callee (var);
+	    tree callee_var = call_edge->map_expr_from_caller_to_callee (var,
+									 NULL);
 	    if (callee_var == NULL_TREE)
 	      {
 		log ("event: %i"
@@ -3273,7 +3274,7 @@ engine::prune_path (checker_path *path,
 		  const callgraph_superedge& cg_superedge
 		    = event->get_callgraph_superedge ();
 		  tree caller_var
-		    = cg_superedge.map_expr_from_callee_to_caller (var);
+		    = cg_superedge.map_expr_from_callee_to_caller (var, NULL);
 		  if (caller_var)
 		    var = caller_var;
 		}
@@ -3285,7 +3286,7 @@ engine::prune_path (checker_path *path,
 		  const callgraph_superedge& cg_superedge
 		    = event->get_callgraph_superedge ();
 		  tree callee_var
-		    = cg_superedge.map_expr_from_caller_to_callee (var);
+		    = cg_superedge.map_expr_from_caller_to_callee (var, NULL);
 		  if (callee_var)
 		    var = callee_var;
 		}
@@ -3434,7 +3435,7 @@ engine::maybe_emit (const candidate_diagnostic &cd)
 
   char *msg = NULL;
   label_text ev_desc
-    = cd.m_pd->describe_final_event (final_event (cd.m_var, cd.m_state));
+    = cd.m_pd->describe (evdesc::final_event (cd.m_var, cd.m_state));
   if (ev_desc.m_buffer)
     {
       if (flag_analyzer_verbose_state_changes)
diff --git a/gcc/analyzer/impl-3.cc b/gcc/analyzer/impl-3.cc
index b1a71e7..6453c42 100644
--- a/gcc/analyzer/impl-3.cc
+++ b/gcc/analyzer/impl-3.cc
@@ -346,8 +346,8 @@ public:
 	if (m_pending_diagnostic)
 	  {
 	    label_text custom_desc
-	      = m_pending_diagnostic->describe_critical_state_change
-		  (state_change (m_var, m_from, m_to, m_emission_id));
+	      = m_pending_diagnostic->describe
+		  (evdesc::state_change (m_var, m_from, m_to, m_emission_id));
 	    if (custom_desc.m_buffer)
 	      {
 		if (flag_analyzer_verbose_state_changes)
@@ -664,7 +664,7 @@ public:
 			pending_diagnostic *pd)
   {
     char *msg = NULL;
-    label_text ev_desc = pd->describe_final_event (final_event (var, state));
+    label_text ev_desc = pd->describe (evdesc::final_event (var, state));
     if (ev_desc.m_buffer)
       {
 	if (flag_analyzer_verbose_state_changes)
@@ -1421,7 +1421,7 @@ engine::prune_path (checker3_path *path,
 		  const callgraph_superedge& cg_superedge
 		    = event->get_callgraph_superedge ();
 		  tree caller_var
-		    = cg_superedge.map_expr_from_callee_to_caller (var);
+		    = cg_superedge.map_expr_from_callee_to_caller (var, NULL);
 		  if (caller_var)
 		    var = caller_var;
 		}
@@ -1433,7 +1433,7 @@ engine::prune_path (checker3_path *path,
 		  const callgraph_superedge& cg_superedge
 		    = event->get_callgraph_superedge ();
 		  tree callee_var
-		    = cg_superedge.map_expr_from_caller_to_callee (var);
+		    = cg_superedge.map_expr_from_caller_to_callee (var, NULL);
 		  if (callee_var)
 		    var = callee_var;
 		}
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 01d1073..d1c50ec 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -1377,8 +1377,8 @@ public:
 	if (m_pending_diagnostic)
 	  {
 	    label_text custom_desc
-	      = m_pending_diagnostic->describe_critical_state_change
-		  (state_change (m_var, m_from, m_to, m_emission_id));
+	      = m_pending_diagnostic->describe
+		  (evdesc::state_change (m_var, m_from, m_to, m_emission_id));
 	    if (custom_desc.m_buffer)
 	      {
 		if (flag_analyzer_verbose_state_changes)
@@ -1432,24 +1432,35 @@ public:
 class edge_event : public checker4_event
 {
 public:
-  edge_event (const superedge *edge,
+  edge_event (const exploded_edge *eedge,
 	      location_t loc, tree fndecl, int depth)
   : checker4_event (EK_EDGE,
 		   loc, fndecl, depth),
-    m_edge (edge)
+    m_eedge (eedge), m_sedge (eedge->m_sedge),
+    m_var (NULL_TREE), m_critical_state (0)
   {
   }
 
+  /* Mark this edge event as being either an interprocedural call or
+     return in which VAR is in STATE, and that this is critical to the
+     diagnostic (so that get_desc can attempt to get a better description
+     from any pending_diagnostic).  */
+  void record_critical_state (tree var, state_machine::state_t state)
+  {
+    m_var = var;
+    m_critical_state = state;
+  }
+
   label_text get_desc () const FINAL OVERRIDE
   {
-    switch (m_edge->m_kind)
+    switch (m_sedge->m_kind)
       {
       default:
 	gcc_unreachable ();
       case SUPEREDGE_CFG_EDGE:
 	{
 	  bool user_facing = !flag_analyzer_verbose_edges;
-	  char *edge_desc = m_edge->get_description (user_facing);
+	  char *edge_desc = m_sedge->get_description (user_facing);
 	  if (user_facing)
 	    {
 	      if (edge_desc && strlen (edge_desc) > 0)
@@ -1473,8 +1484,8 @@ public:
 		  label_text result
 		    = label_text::take (print ("taking %qs edge SN:%i -> SN:%i",
 					       edge_desc,
-					       m_edge->m_src->m_index,
-					       m_edge->m_dest->m_index));
+					       m_sedge->m_src->m_index,
+					       m_sedge->m_dest->m_index));
 		  free (edge_desc);
 		  return result;
 		}
@@ -1482,62 +1493,89 @@ public:
 		{
 		  free (edge_desc);
 		  return label_text::take (print ("taking edge SN:%i -> SN:%i",
-						  m_edge->m_src->m_index,
-						  m_edge->m_dest->m_index));
+						  m_sedge->m_src->m_index,
+						  m_sedge->m_dest->m_index));
 		}
 	    }
 	}
       case SUPEREDGE_CALL:
 	{
+	  if (m_critical_state && m_pending_diagnostic)
+	    {
+	      gcc_assert (m_var);
+	      label_text custom_desc
+		= m_pending_diagnostic->describe
+		    (evdesc::call_with_state (m_sedge->m_src->m_fun->decl,
+					      m_sedge->m_dest->m_fun->decl,
+					      m_var,
+					      m_critical_state));
+	      if (custom_desc.m_buffer)
+		return custom_desc;
+	    }
+
 	  return label_text::take (print ("calling %qE from %qE",
-					  m_edge->m_dest->m_fun->decl,
-					  m_edge->m_src->m_fun->decl));
+					  m_sedge->m_dest->m_fun->decl,
+					  m_sedge->m_src->m_fun->decl));
 	}
 
       case SUPEREDGE_RETURN:
 	{
+	  /*  For greatest precision-of-wording, if this is returning the
+	      state involved in the pending diagnostic, give the pending
+	      diagnostic a chance to describe this return (in terms of
+	      itself).  */
+	  if (m_critical_state && m_pending_diagnostic)
+	    {
+	      label_text custom_desc
+		= m_pending_diagnostic->describe
+		    (evdesc::return_of_state (m_sedge->m_dest->m_fun->decl,
+					      m_sedge->m_src->m_fun->decl,
+					      m_critical_state));
+	      if (custom_desc.m_buffer)
+		return custom_desc;
+	    }
 	  return label_text::take (print ("returning to %qE from %qE",
-					  m_edge->m_dest->m_fun->decl,
-					  m_edge->m_src->m_fun->decl));
+					  m_sedge->m_dest->m_fun->decl,
+					  m_sedge->m_src->m_fun->decl));
 	}
 
       case SUPEREDGE_INTRAPROCEDURAL_CALL:
 	return label_text::take (// TODO: possibly a higher-level description?
 				 print ("taking edge SN:%i -> SN:%i",
-					m_edge->m_src->m_index,
-					m_edge->m_dest->m_index));
+					m_sedge->m_src->m_index,
+					m_sedge->m_dest->m_index));
       }
   }
 
   const cfg_superedge& get_cfg_superedge () const
   {
-    gcc_assert (m_edge->m_kind == SUPEREDGE_CFG_EDGE);
-    return *m_edge->dyn_cast_cfg_superedge ();
+    gcc_assert (m_sedge->m_kind == SUPEREDGE_CFG_EDGE);
+    return *m_sedge->dyn_cast_cfg_superedge ();
   }
 
   const callgraph_superedge& get_callgraph_superedge () const
   {
-    gcc_assert (m_edge->m_kind != SUPEREDGE_CFG_EDGE);
-    return *m_edge->dyn_cast_callgraph_superedge ();
+    gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE);
+    return *m_sedge->dyn_cast_callgraph_superedge ();
   }
 
   checker4_event *clone () const FINAL OVERRIDE
   {
-    return new edge_event (m_edge, m_loc, m_fndecl, m_depth);
+    return new edge_event (m_eedge, m_loc, m_fndecl, m_depth);
   }
 
   bool is_call_p () const FINAL OVERRIDE
   {
-    return m_edge->m_kind == SUPEREDGE_CALL;
+    return m_sedge->m_kind == SUPEREDGE_CALL;
   }
   bool is_return_p () const FINAL OVERRIDE
   {
-    return m_edge->m_kind == SUPEREDGE_RETURN;
+    return m_sedge->m_kind == SUPEREDGE_RETURN;
   }
 
   bool should_filter_p (int verbosity) const
   {
-    switch (m_edge->m_kind)
+    switch (m_sedge->m_kind)
       {
       case SUPEREDGE_CFG_EDGE:
 	{
@@ -1563,7 +1601,10 @@ public:
     return false;
   }
 
-  const superedge *m_edge;
+  const exploded_edge *m_eedge;
+  const superedge *m_sedge;
+  tree m_var;
+  state_machine::state_t m_critical_state;
 };
 
 /* FIXME: a repeat of the warning message at the end of the path.  */
@@ -1669,7 +1710,7 @@ public:
 			pending_diagnostic *pd)
   {
     char *msg = NULL;
-    label_text ev_desc = pd->describe_final_event (final_event (var, state));
+    label_text ev_desc = pd->describe (evdesc::final_event (var, state));
     if (ev_desc.m_buffer)
       {
 	if (sm && flag_analyzer_verbose_state_changes)
@@ -4087,7 +4128,7 @@ diagnostic_manager::build_emission_path (const exploded_graph &eg,
 			const gimple *last_stmt
 			  = src_point.m_supernode->get_last_stmt ();
 			emission_path->add_event
-			  (new edge_event (eedge->m_sedge,
+			  (new edge_event (eedge,
 					   (last_stmt
 					    ? last_stmt->location
 					    : UNKNOWN_LOCATION),
@@ -4104,7 +4145,7 @@ diagnostic_manager::build_emission_path (const exploded_graph &eg,
 
 			const gcall *call_stmt = return_edge->get_call_stmt ();
 			emission_path->add_event
-			  (new edge_event (eedge->m_sedge,
+			  (new edge_event (eedge,
 					   (call_stmt
 					    ? call_stmt->location
 					    : UNKNOWN_LOCATION),
@@ -4259,8 +4300,7 @@ void
 diagnostic_manager::prune_path (checker4_path *path,
 				const state_machine *sm,
 				tree var,
-				state_machine::state_t state
-				  ATTRIBUTE_UNUSED) const
+				state_machine::state_t state) const
 {
   JIT_LOG_FUNC (get_logger ());
   path->maybe_log (get_logger (), "path");
@@ -4334,6 +4374,10 @@ diagnostic_manager::prune_path (checker4_path *path,
 			 idx, var, state_change->m_origin);
 		    var = state_change->m_origin;
 		  }
+		log ("event %i: switching state of interest from %qs to %qs",
+		     idx, sm->get_state_name (state_change->m_to),
+		     sm->get_state_name (state_change->m_from));
+		state = state_change->m_from;
 	      }
 	    else if (m_verbosity < 3)
 	      {
@@ -4352,17 +4396,27 @@ diagnostic_manager::prune_path (checker4_path *path,
 	case EK_EDGE:
 	  {
 	    edge_event *event = (edge_event *)base_event;
+
+	    const program_state &src_state
+	      = event->m_eedge->m_src->get_state ();
+#if 0
+	      svalue_id src_sid
+		= src_state.m_region_model->get_rvalue (dst_rep, NULL);
+	      if (src_sid.null_p ())
+		continue;
+#endif
+
 #if 0
 	    inform (base_event->m_loc, "considering edge_event %i: %s",
 		    idx, base_event->m_desc);
 #endif
-	    switch (event->m_edge->m_kind)
+	    switch (event->m_sedge->m_kind)
 	      {
 	      case SUPEREDGE_CFG_EDGE:
 		{
 		  const cfg_superedge& cfg_superedge
 		    = event->get_cfg_superedge ();
-		  const supernode *dest = event->m_edge->m_dest;
+		  const supernode *dest = event->m_sedge->m_dest;
 		  /* Do we have an SSA_NAME defined via a phi node in
 		     the dest CFG node?  */
 		  if (var && TREE_CODE (var) == SSA_NAME)
@@ -4403,10 +4457,30 @@ diagnostic_manager::prune_path (checker4_path *path,
 		{
 		  const callgraph_superedge& cg_superedge
 		    = event->get_callgraph_superedge ();
+		  callsite_expr expr;
 		  tree caller_var
-		    = cg_superedge.map_expr_from_callee_to_caller (var);
+		    = cg_superedge.map_expr_from_callee_to_caller (var, &expr);
 		  if (caller_var)
-		    var = caller_var;
+		    {
+		      log ("event %i:"
+			   " switching var of interest"
+			   " from %qE in callee to %qE in caller",
+			   idx, var, caller_var);
+		      var = caller_var;
+		      if (expr.param_p ())
+			event->record_critical_state (var, state);
+		    }
+		  //#error
+		  /* potentially use this for precision-of-wording:
+		     identify passing a parm (and which parm idx)
+		     then update the edge_event to note that it's a critical
+		     state-related edge (with the detail, maybe as an optional
+		     object).
+		     Then update the get_desc to use the detail, passing
+		     it to the pending_diagnostic to get a precise message
+		     e.g. passing possibly-NULL pointer 'ptr' to 'foo' from 'bar' as param 1
+		     or somesuch
+		   */
 		}
 		break;
 	      case SUPEREDGE_RETURN:
@@ -4415,10 +4489,21 @@ diagnostic_manager::prune_path (checker4_path *path,
 		{
 		  const callgraph_superedge& cg_superedge
 		    = event->get_callgraph_superedge ();
+		  callsite_expr expr;
 		  tree callee_var
-		    = cg_superedge.map_expr_from_caller_to_callee (var);
+		    = cg_superedge.map_expr_from_caller_to_callee (var, &expr);
 		  if (callee_var)
-		    var = callee_var;
+		    {
+		      log ("event %i:"
+			   " switching var of interest"
+			   " from %qE in caller to %qE in callee",
+			   idx, var, callee_var);
+		      var = callee_var;
+		      if (expr.return_value_p ())
+			event->record_critical_state (var, state);
+		    }
+		  /* FIXME: similar precision-of-wording notes to above,
+		     but for return value.  */
 		}
 		break;
 	      case SUPEREDGE_INTRAPROCEDURAL_CALL:
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 1733fe6..2c2535d 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "supergraph.h"
 #include "analyzer-logging.h"
 #include "tree-diagnostic.h"
+#include "tree-dfa.h"
 
 /* Get the cgraph_edge, but only if there's an underlying function body.  */
 
@@ -895,7 +896,8 @@ callgraph_superedge::get_caller_decl () const
 /* FIXME.  */
 
 tree
-callgraph_superedge::get_arg_for_parm (tree parm_to_find) const
+callgraph_superedge::get_arg_for_parm (tree parm_to_find,
+				       callsite_expr *out) const
 {
   gcc_assert  (TREE_CODE (parm_to_find) == PARM_DECL);
 
@@ -906,7 +908,11 @@ callgraph_superedge::get_arg_for_parm (tree parm_to_find) const
        iter_parm = DECL_CHAIN (iter_parm), ++i)
     {
       if (iter_parm == parm_to_find)
-	return gimple_call_arg (get_call_stmt (), i);
+	{
+	  if (out)
+	    *out = callsite_expr::from_zero_based_param (i);
+	  return gimple_call_arg (get_call_stmt (), i);
+	}
     }
 
   /* Not found.  */
@@ -916,7 +922,8 @@ callgraph_superedge::get_arg_for_parm (tree parm_to_find) const
 /* FIXME.  */
 
 tree
-callgraph_superedge::get_parm_for_arg (tree arg_to_find) const
+callgraph_superedge::get_parm_for_arg (tree arg_to_find,
+				       callsite_expr *out) const
 {
   tree callee = get_callee_decl ();
 
@@ -924,8 +931,15 @@ callgraph_superedge::get_parm_for_arg (tree arg_to_find) const
   for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm;
        iter_parm = DECL_CHAIN (iter_parm), ++i)
     {
-      if (arg_to_find == gimple_call_arg (get_call_stmt (), i))
-	return iter_parm;
+      tree param = gimple_call_arg (get_call_stmt (), i);
+      if (arg_to_find == param)
+	{
+	  if (out)
+	    *out = callsite_expr::from_zero_based_param (i);
+	  //return iter_parm;
+	  // FIXME: should we get at default def:
+	  return ssa_default_def (get_callee_function (), iter_parm);
+	}
     }
 
   // FIXME: what if more than one?
@@ -934,38 +948,57 @@ callgraph_superedge::get_parm_for_arg (tree arg_to_find) const
   return NULL_TREE;
 }
 
-/* Map caller_expr back to an expr within the callee, or return NULL_TREE.  */
-
+/* Map caller_expr back to an expr within the callee, or return NULL_TREE.
+   If non-NULL is returned, populate OUT.  */
+//#error
 tree
-callgraph_superedge::map_expr_from_caller_to_callee (tree caller_expr) const
+callgraph_superedge::map_expr_from_caller_to_callee (tree caller_expr,
+						     callsite_expr *out) const
 {
   /* Is it an argument (actual param)?  If so, convert to
      parameter (formal param).  */
-  tree parm = get_parm_for_arg (caller_expr);
+  tree parm = get_parm_for_arg (caller_expr, out);
   if (parm)
     return parm;
   /* Otherwise try return value.  */
   if (caller_expr == gimple_call_lhs (get_call_stmt ()))
-    return DECL_RESULT (get_callee_decl ());
+    {
+      if (out)
+	*out = callsite_expr::from_return_value ();
+      return DECL_RESULT (get_callee_decl ());
+    }
 
   return NULL_TREE;
 }
 
-/* Map callee_expr back to an expr within the caller, or return NULL_TREE.  */
+/* Map callee_expr back to an expr within the caller, or return NULL_TREE.
+   If non-NULL is returned, populate OUT.  */
+//#error
 
 tree
-callgraph_superedge::map_expr_from_callee_to_caller (tree callee_expr) const
+callgraph_superedge::map_expr_from_callee_to_caller (tree callee_expr,
+						     callsite_expr *out) const
 {
   if (callee_expr == NULL_TREE)
     return NULL_TREE;
 
   /* If it's a parameter (formal param), get the argument (actual param).  */
   if (TREE_CODE (callee_expr) == PARM_DECL)
-    return get_arg_for_parm (callee_expr);
+    return get_arg_for_parm (callee_expr, out);
+
+  /* Similar for the default SSA name of the PARM_DECL.  */
+  if (TREE_CODE (callee_expr) == SSA_NAME
+      && SSA_NAME_IS_DEFAULT_DEF (callee_expr)
+      && TREE_CODE (SSA_NAME_VAR (callee_expr)) == PARM_DECL)
+    return get_arg_for_parm (SSA_NAME_VAR (callee_expr), out);
 
   /* Otherwise try return value.  */
   if (callee_expr == DECL_RESULT (get_callee_decl ()))
-    return gimple_call_lhs (get_call_stmt ());
+    {
+      if (out)
+	*out = callsite_expr::from_return_value ();
+      return gimple_call_lhs (get_call_stmt ());
+    }
 
   return NULL_TREE;
 }
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index d6e93aa..01a0cb4 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -292,6 +292,40 @@ class superedge
   const enum edge_kind m_kind;
 };
 
+/* FIXME.  */
+// Either a parm, or the return value.  */
+
+class callsite_expr
+{
+ public:
+  callsite_expr () : m_val (-1) {}
+
+  static callsite_expr from_zero_based_param (int idx)
+  {
+    return callsite_expr (idx + 1);
+  }
+
+  static callsite_expr from_return_value ()
+  {
+    return callsite_expr (0);
+  }
+
+  bool param_p () const
+  {
+    return m_val > 0;
+  }
+
+  bool return_value_p () const
+  {
+    return m_val == 0;
+  }
+
+ private:
+  callsite_expr (int val) : m_val (val) {}
+
+  int m_val; /* 1-based parm, 0 for return value, or -1 for "unknown".  */
+};
+
 /* A subclass of superedge corresponding to a callgraph edge.  */
 
 class callgraph_superedge : public superedge
@@ -310,10 +344,12 @@ class callgraph_superedge : public superedge
   tree get_callee_decl () const;
   tree get_caller_decl () const;
   gcall *get_call_stmt () const { return m_cedge->call_stmt; }
-  tree get_arg_for_parm (tree parm) const;
-  tree get_parm_for_arg (tree arg) const;
-  tree map_expr_from_caller_to_callee (tree caller_expr) const;
-  tree map_expr_from_callee_to_caller (tree callee_expr) const;
+  tree get_arg_for_parm (tree parm, callsite_expr *out) const;
+  tree get_parm_for_arg (tree arg, callsite_expr *out) const;
+  tree map_expr_from_caller_to_callee (tree caller_expr,
+				       callsite_expr *out) const;
+  tree map_expr_from_callee_to_caller (tree callee_expr,
+				       callsite_expr *out) const;
 
   cgraph_edge *const m_cedge;
 };
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c
index 14478cf..d9e8d60 100644
--- a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c
@@ -46,7 +46,7 @@ void test_1 (void *ptr, int a, int b)
       |   NN |     calls_free_1 (ptr);
       |      |     ~~~~~~~~~~~~~~~~~~
       |      |     |
-      |      |     (4) calling 'calls_free_1' from 'test_1'
+      |      |     (4) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
       |
       +--> 'calls_free_1': event 5
                   |
@@ -114,7 +114,7 @@ void test_2 (void *ptr, int a, int b)
       |   NN |       calls_free_2 (ptr);
       |      |       ~~~~~~~~~~~~~~~~~~
       |      |       |
-      |      |       (4) calling 'calls_free_2' from 'test_2'
+      |      |       (4) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
       |
       +--> 'calls_free_2': event 5
                   |
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c
index 6551588..3313670 100644
--- a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c
@@ -56,7 +56,7 @@ void test_1 (void *ptr, int a, int b)
       |   NN |     calls_free_1 (ptr);
       |      |     ~~~~~~~~~~~~~~~~~~
       |      |     |
-      |      |     (6) calling 'calls_free_1' from 'test_1'
+      |      |     (6) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
       |
       +--> 'calls_free_1': events 7-8
                   |
@@ -139,7 +139,7 @@ void test_2 (void *ptr, int a, int b)
       |   NN |       calls_free_2 (ptr);
       |      |       ~~~~~~~~~~~~~~~~~~
       |      |       |
-      |      |       (6) calling 'calls_free_2' from 'test_2'
+      |      |       (6) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
       |
       +--> 'calls_free_2': events 7-8
                   |
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c
index 3a10497..a4e9bde 100644
--- a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c
@@ -65,7 +65,7 @@ void test_1 (void *ptr, int a, int b)
       |   NN |     calls_free_1 (ptr);
       |      |     ~~~~~~~~~~~~~~~~~~
       |      |     |
-      |      |     (8) calling 'calls_free_1' from 'test_1'
+      |      |     (8) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
       |
       +--> 'calls_free_1': events 9-10
                   |
@@ -158,7 +158,7 @@ void test_2 (void *ptr, int a, int b)
       |   NN |       calls_free_2 (ptr);
       |      |       ~~~~~~~~~~~~~~~~~~
       |      |       |
-      |      |       (8) calling 'calls_free_2' from 'test_2'
+      |      |       (8) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
       |
       +--> 'calls_free_2': events 9-10
                   |
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c
index a88263f..212097c 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c
@@ -26,7 +26,7 @@ void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */
 
   do_stuff ();
 
-  calls_free (ptr); /* { dg-message "\\(6\\) calling 'calls_free' from 'test'" } */
+  calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */
 
   do_stuff ();
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c
index 835d3b5..112b6d8 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c
@@ -71,7 +71,7 @@ void test (void *ptr)
      |   NN |   may_call_free (ptr);
      |      |   ~~~~~~~~~~~~~~~~~~~
      |      |   |
-     |      |   (7) calling 'may_call_free' from 'test'
+     |      |   (7) passing freed pointer 'ptr' in call to 'may_call_free' from 'test'
      |
      +--> 'may_call_free': events 8-10
                  |
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c
new file mode 100644
index 0000000..c45efbc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c
@@ -0,0 +1,30 @@
+/* { dg-options "--analyzer -fdiagnostics-path-summary-format=none -fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+calls_free (void *victim)
+{
+  free (victim); /* { dg-warning "double-free of 'victim'" } */
+}
+
+extern void do_stuff (void);
+
+struct foo
+{
+  void *m_p;
+};
+
+void test (struct foo f)
+{
+  do_stuff ();
+
+  calls_free (f.m_p);
+
+  do_stuff ();
+
+  calls_free (f.m_p); /* { dg-message "passing freed pointer '<unknown>' in call to 'calls_free' from 'test'" } */
+  // TODO: something better than '<unknown>'
+
+  do_stuff ();
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c
index f42042d..7edd0b0 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c
@@ -25,7 +25,7 @@ void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */
 
   do_stuff ();
 
-  calls_free (ptr); /* { dg-message "\\(6\\) calling 'calls_free' from 'test'" } */
+  calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */
 
   do_stuff ();
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
index b88dd5d..a4ab163 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
@@ -58,7 +58,7 @@ void test (int i)
      |      |                    |
      |      |                    (2) calling 'make_boxed_int' from 'test'
      |
-     +--> 'make_boxed_int': events 3-4
+     +--> 'make_boxed_int': events 3-5
                   |
                   |   NN | make_boxed_int (int i)
                   |      | ^~~~~~~~~~~~~~
@@ -69,90 +69,98 @@ void test (int i)
                   |      |      ~
                   |      |      |
                   |      |      (4) following 'false' branch
+                  |......
+                  |   NN |   return result;
+                  |      |          ~~~~~~
+                  |      |          |
+                  |      |          (5) state of '<return-value>': 'start' -> 'nonnull' (origin: NULL)
                   |
      <------------+
      |
-  'test': events 5-6
+  'test': events 6-7
      |
      |   NN |   boxed_int *obj = make_boxed_int (i);
      |      |                    ^~~~~~~~~~~~~~~~~~
      |      |                    |
-     |      |                    (5) returning to 'test' from 'make_boxed_int'
+     |      |                    (6) returning to 'test' from 'make_boxed_int'
      |   NN | 
      |   NN |   free_boxed_int (obj);
      |      |   ~~~~~~~~~~~~~~~~~~~~
      |      |   |
-     |      |   (6) calling 'free_boxed_int' from 'test'
+     |      |   (7) calling 'free_boxed_int' from 'test'
      |
-     +--> 'free_boxed_int': events 7-8
+     +--> 'free_boxed_int': events 8-9
                   |
                   |   NN | free_boxed_int (boxed_int *bi)
                   |      | ^~~~~~~~~~~~~~
                   |      | |
-                  |      | (7) entry to 'free_boxed_int'
+                  |      | (8) entry to 'free_boxed_int'
                   |   NN | {
                   |   NN |   wrapped_free (bi);
                   |      |   ~~~~~~~~~~~~~~~~~
                   |      |   |
-                  |      |   (8) calling 'wrapped_free' from 'free_boxed_int'
+                  |      |   (9) calling 'wrapped_free' from 'free_boxed_int'
                   |
-                  +--> 'wrapped_free': events 9-10
+                  +--> 'wrapped_free': events 10-11
                               |
                               |   NN | void wrapped_free (void *ptr)
                               |      |      ^~~~~~~~~~~~
                               |      |      |
-                              |      |      (9) entry to 'wrapped_free'
+                              |      |      (10) entry to 'wrapped_free'
                               |   NN | {
                               |   NN |   free (ptr);
                               |      |   ~~~~~~~~~~
                               |      |   |
-                              |      |   (10) first 'free' here (state of 'ptr': 'nonnull' -> 'free', origin: NULL)
+                              |      |   (11) first 'free' here (state of 'ptr': 'nonnull' -> 'free', origin: NULL)
                               |
                   <-----------+
                   |
-          'free_boxed_int': event 11
+          'free_boxed_int': event 12
                   |
                   |   NN |   wrapped_free (bi);
                   |      |   ^~~~~~~~~~~~~~~~~
                   |      |   |
-                  |      |   (11) returning to 'free_boxed_int' from 'wrapped_free'
+                  |      |   (12) returning to 'free_boxed_int' from 'wrapped_free'
                   |
      <------------+
      |
-  'test': events 12-13
+  'test': events 13-14
      |
      |   NN |   free_boxed_int (obj);
      |      |   ^~~~~~~~~~~~~~~~~~~~
      |      |   |
-     |      |   (12) returning to 'test' from 'free_boxed_int'
+     |      |   (13) returning to 'test' from 'free_boxed_int'
      |   NN | 
      |   NN |   free_boxed_int (obj);
      |      |   ~~~~~~~~~~~~~~~~~~~~
      |      |   |
-     |      |   (13) calling 'free_boxed_int' from 'test'
+     |      |   (14) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test'
      |
-     +--> 'free_boxed_int': events 14-15
+     +--> 'free_boxed_int': events 15-16
                   |
                   |   NN | free_boxed_int (boxed_int *bi)
                   |      | ^~~~~~~~~~~~~~
                   |      | |
-                  |      | (14) entry to 'free_boxed_int'
+                  |      | (15) entry to 'free_boxed_int'
                   |   NN | {
                   |   NN |   wrapped_free (bi);
                   |      |   ~~~~~~~~~~~~~~~~~
                   |      |   |
-                  |      |   (15) calling 'wrapped_free' from 'free_boxed_int'
+                  |      |   (16) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int'
                   |
-                  +--> 'wrapped_free': events 16-17
+                  +--> 'wrapped_free': events 17-18
                               |
                               |   NN | void wrapped_free (void *ptr)
                               |      |      ^~~~~~~~~~~~
                               |      |      |
-                              |      |      (16) entry to 'wrapped_free'
+                              |      |      (17) entry to 'wrapped_free'
                               |   NN | {
                               |   NN |   free (ptr);
                               |      |   ~~~~~~~~~~
                               |      |   |
-                              |      |   (17) second 'free' here ('ptr' is in state 'free')
+                              |      |   (18) second 'free' here ('ptr' is in state 'free')
                               |
    { dg-end-multiline-output "" } */
+
+/* TODO: the event describing the allocation is uninteresting and probably
+   should be purged.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
index facd815..eff358a 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
@@ -56,7 +56,7 @@ make_boxed_int (int i)
           |   NN |   boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
           |      |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |                                    |
-          |      |                                    (5) returning to 'make_boxed_int' from 'wrapped_malloc'
+          |      |                                    (5) possible return of NULL to 'make_boxed_int' from 'wrapped_malloc'
           |   NN |   result->i = i;
           |      |   ~~~~~~~~~~~~~                     
           |      |             |
-- 
1.8.5.3

