From 27c025429927c92daf64c04e0738591c35921457 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 15 Jul 2020 15:59:03 -0400
Subject: [PATCH 305/377] FIXME: add svalue complexity limit

---
 gcc/analyzer/analyzer.opt                     |   4 +
 gcc/analyzer/region-model2.cc                 | 144 +++++++++++++++++-
 gcc/analyzer/region-model2.h                  | 119 +++++++++++----
 .../gcc.dg/analyzer/use-after-free.c          |  12 ++
 4 files changed, 243 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/use-after-free.c

diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 22cf4b0ad3b..39775bd83f3 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -34,6 +34,10 @@ The maximum number of exploded nodes per program point within the analyzer, befo
 Common Joined UInteger Var(param_analyzer_max_recursion_depth) Init(2) Param
 The maximum number of times a callsite can appear in a call stack within the analyzer, before terminating analysis of a call that would recurse deeper.
 
+-param=analyzer-max-svalue-depth=
+Common Joined UInteger Var(param_analyzer_max_svalue_depth) Init(15) Param
+The maximum depth of a symbolic value, before approximating the value as unknown.
+
 -param=analyzer-min-snodes-for-call-summary=
 Common Joined UInteger Var(param_analyzer_min_snodes_for_call_summary) Init(10) Param
 The minimum number of supernodes within a function for the analyzer to consider summarizing its effects at call sites.
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 03919488f2d..a6aac080615 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -155,6 +155,36 @@ class impl_constraint_manager : public constraint_manager
 };
 #endif
 
+/* struct complexity.  */
+
+/* Get complexity for a new node that references REG
+   (the complexity of REG, plus one for the new node).  */
+
+complexity::complexity (const region2 *reg)
+: m_num_nodes (reg->get_complexity ().m_num_nodes + 1),
+  m_max_depth (reg->get_complexity ().m_max_depth + 1)
+{
+}
+
+/* Get complexity for a new node that references SVAL.
+   (the complexity of SVAL, plus one for the new node).  */
+
+complexity::complexity (const svalue2 *sval)
+: m_num_nodes (sval->get_complexity ().m_num_nodes + 1),
+  m_max_depth (sval->get_complexity ().m_max_depth + 1)
+{
+}
+
+/* Get complexity for a new node that references nodes with complexity
+   C1 and C2.  */
+
+complexity
+complexity::from_pair (const complexity &c1, const complexity &c2)
+{
+  return complexity (c1.m_num_nodes + c2.m_num_nodes + 1,
+		     MAX (c1.m_max_depth, c2.m_max_depth) + 1);
+}
+
 /* class svalue2 and its various subclasses.  */
 
 /* class svalue2.  */
@@ -775,6 +805,15 @@ binop_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
 
 /* class sub_svalue2 : public svalue2.  */
 
+sub_svalue2::sub_svalue2 (tree type, const svalue2 *parent_svalue,
+			  const region2 *subregion)
+: svalue2 (complexity::from_pair (parent_svalue->get_complexity (),
+				  subregion->get_complexity ()),
+	   type),
+  m_parent_svalue (parent_svalue), m_subregion (subregion)
+{
+}
+
 void
 sub_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
 {
@@ -1002,6 +1041,23 @@ compound_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
+/* FIXME.  */
+
+complexity
+compound_svalue2::calc_complexity (const binding_map &map)
+{
+  unsigned num_child_nodes = 0;
+  unsigned max_child_depth = 0;
+  for (binding_map::iterator_t iter = map.begin ();
+       iter != map.end (); ++iter)
+    {
+      const complexity &sval_c = (*iter).second->get_complexity ();
+      num_child_nodes += sval_c.m_num_nodes;
+      max_child_depth = MAX (max_child_depth, sval_c.m_max_depth);
+    }
+  return complexity (num_child_nodes + 1, max_child_depth + 1);
+}
+
 /* class region2 and its various subclasses.  */
 
 /* Get a string for KIND for use in debug dumps.  */
@@ -2101,8 +2157,9 @@ region2::symbolic_for_unknown_ptr_p () const
 
 /* region2's ctor.  */
 
-region2::region2 (unsigned id, const region2 *parent, tree type)
-: m_id (id), m_parent (parent), m_type (type), m_cached_offset (NULL)
+region2::region2 (complexity c, unsigned id, const region2 *parent, tree type)
+: m_complexity (c), m_id (id), m_parent (parent), m_type (type),
+  m_cached_offset (NULL)
 {
   gcc_assert (type == NULL_TREE || TYPE_P (type));
 }
@@ -2601,7 +2658,7 @@ heap_region2::dump_to_pp (pretty_printer *pp, bool simple) const
 /* root_region2's default ctor.  */
 
 root_region2::root_region2 (unsigned id)
-: region2 (id, NULL, NULL_TREE)
+: region2 (complexity (1, 1), id, NULL, NULL_TREE)
 {
 }
 
@@ -3156,6 +3213,7 @@ region_model2_manager::region_model2_manager ()
   m_stack_region (alloc_region_id (), &m_root_region),
   m_heap_region (alloc_region_id (), &m_root_region),
   m_unknown_NULL (NULL),
+  m_max_complexity (0, 0),
   m_code_region (alloc_region_id (), &m_root_region),
   m_fndecls_map (), m_labels_map (),
   m_globals_region (alloc_region_id (), &m_root_region),
@@ -3222,6 +3280,70 @@ region_model2_manager::~region_model2_manager ()
     delete (*iter).second;
 }
 
+/* Return true if C exceeds the complexity limit for svalues.  */
+
+bool
+region_model2_manager::too_complex_p (const complexity &c) const
+{
+  /* TODO: check both num nodes and max_depth
+     TODO: use params.  */
+#if 0
+  if (c.m_num_nodes > 50)
+    return true;
+#endif
+#if 1
+  if (c.m_max_depth > param_analyzer_max_svalue_depth)
+    return true;
+#endif
+  return false;
+}
+
+/* If SVAL exceeds the complexity limit for svalues, delete it
+   and return true.
+   Otherwise update m_max_complexity and return false.  */
+
+bool
+region_model2_manager::reject_if_too_complex (svalue2 *sval)
+{
+  const complexity &c = sval->get_complexity ();
+  if (!too_complex_p (c))
+    {
+      if (m_max_complexity.m_num_nodes < c.m_num_nodes)
+	m_max_complexity.m_num_nodes = c.m_num_nodes;
+      if (m_max_complexity.m_max_depth < c.m_max_depth)
+	m_max_complexity.m_max_depth = c.m_max_depth;
+      return false;
+    }
+
+  /* TODO.  */
+  if (0)
+    {
+      pretty_printer pp;
+      pp_format_decoder (&pp) = default_tree_printer;
+      sval->dump_to_pp (&pp, true);
+      warning (0, "svalue is too complicated: %qs", pp_formatted_text (&pp));
+    }
+
+  delete sval;
+
+  return true;
+}
+
+/* Macro for imposing a complexity limit on svalues, for use within
+   region_model2_manager member functions.
+
+   If SVAL is exceeds the complexity limit, delete it and return an UNKNOWN
+   value of the same type.
+   Otherwise update m_max_complexity and carry on.  */
+
+#define RETURN_UNKNOWN_IF_TOO_COMPLEX(SVAL)			\
+  do {								\
+    svalue2 *sval_ = (SVAL);					\
+    tree type_ = sval_->get_type ();				\
+    if (reject_if_too_complex (sval_))				\
+      return get_or_create_unknown_svalue2 (type_);		\
+  } while (0)
+
 /* svalue2 consolidation.  */
 
 /* Return an svalue2 *for a constant_svalue2 for CST_EXPR,
@@ -3237,6 +3359,7 @@ region_model2_manager::get_or_create_constant_svalue2 (tree cst_expr)
   if (slot)
     return *slot;
   constant_svalue2 *cst_sval = new constant_svalue2 (cst_expr);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (cst_sval);
   m_constants_map.put (cst_expr, cst_sval);
   return cst_sval;
   // TODO: cst equality vs tree pointer equality?
@@ -3295,6 +3418,7 @@ region_model2_manager::get_or_create_initial_value (const region2 *reg)
   if (initial_svalue2 **slot = m_initial_values_map.get (reg))
     return *slot;
   initial_svalue2 *initial_sval = new initial_svalue2 (reg->get_type (), reg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (initial_sval);
   m_initial_values_map.put (reg, initial_sval);
   return initial_sval;
 }
@@ -3307,6 +3431,7 @@ region_model2_manager::get_or_create_setjmp_svalue2 (const setjmp_record &r,
   if (setjmp_svalue2 **slot = m_setjmp_values_map.get (key))
     return *slot;
   setjmp_svalue2 *setjmp_sval = new setjmp_svalue2 (r, type);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (setjmp_sval);
   m_setjmp_values_map.put (key, setjmp_sval);
   return setjmp_sval;
 }
@@ -3319,6 +3444,7 @@ region_model2_manager::get_or_create_poisoned_svalue2 (enum poison_kind kind,
   if (poisoned_svalue2 **slot = m_poisoned_values_map.get (key))
     return *slot;
   poisoned_svalue2 *poisoned_sval = new poisoned_svalue2 (kind, type);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (poisoned_sval);
   m_poisoned_values_map.put (key, poisoned_sval);
   return poisoned_sval;
 }
@@ -3336,6 +3462,7 @@ region_model2_manager::get_ptr_svalue2 (tree ptr_type, const region2 *pointee)
   if (region_svalue2 **slot = m_pointer_values_map.get (pointee))
     return *slot;
   region_svalue2 *sval = new region_svalue2 (ptr_type, pointee);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (sval);
   m_pointer_values_map.put (pointee, sval);
   return sval;
 }
@@ -3405,6 +3532,7 @@ region_model2_manager::get_or_create_unaryop (tree type, enum tree_code op,
   if (unaryop_svalue2 **slot = m_unaryop_values_map.get (key))
     return *slot;
   unaryop_svalue2 *unaryop_sval = new unaryop_svalue2 (type, op, arg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (unaryop_sval);
   m_unaryop_values_map.put (key, unaryop_sval);
   return unaryop_sval;
 }
@@ -3542,6 +3670,7 @@ region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
   if (binop_svalue2 **slot = m_binop_values_map.get (key))
     return *slot;
   binop_svalue2 *binop_sval = new binop_svalue2 (type, op, arg0, arg1);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (binop_sval);
   m_binop_values_map.put (key, binop_sval);
   return binop_sval;
 }
@@ -3647,6 +3776,7 @@ region_model2_manager::get_or_create_sub_svalue2 (tree type,
     return *slot;
   sub_svalue2 *sub_sval
     = new sub_svalue2 (type, parent_svalue2, subregion);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (sub_sval);
   m_sub_values_map.put (key, sub_sval);
   return sub_sval;
 }
@@ -3662,6 +3792,7 @@ region_model2_manager::get_or_create_unmergeable (const svalue2 *arg)
   if (unmergeable_svalue2 **slot = m_unmergeable_values_map.get (arg))
     return *slot;
   unmergeable_svalue2 *unmergeable_sval = new unmergeable_svalue2 (arg);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (unmergeable_sval);
   m_unmergeable_values_map.put (arg, unmergeable_sval);
   return unmergeable_sval;
 }
@@ -3679,6 +3810,7 @@ region_model2_manager::get_or_create_widening_svalue2 (tree type,
     return *slot;
   widening_svalue2 *widening_sval
     = new widening_svalue2 (type, point, base_sval, iter_sval);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (widening_sval);
   m_widening_values_map.put (key, widening_sval);
   return widening_sval;
 }
@@ -3695,6 +3827,7 @@ get_or_create_compound_svalue2 (tree type,
     return *slot;
   compound_svalue2 *compound_sval
     = new compound_svalue2 (type, map);
+  RETURN_UNKNOWN_IF_TOO_COMPLEX (compound_sval);
   /* Use make_key rather than reusing the key, so that we use a
      ptr to compound_sval's binding_map, rather than the MAP param.  */
   m_compound_values_map.put (compound_sval->make_key (), compound_sval);
@@ -3986,6 +4119,11 @@ region_model2_manager::log_stats (logger *logger, bool show_objs) const
 		m_unmergeable_values_map);
   log_uniq_map (logger, show_objs, "widening_svalue2", m_widening_values_map);
   log_uniq_map (logger, show_objs, "compound_svalue2", m_compound_values_map);
+  logger->log ("max accepted svalue num_nodes: %i",
+	       m_max_complexity.m_num_nodes);
+  logger->log ("max accepted svalue max_depth: %i",
+	       m_max_complexity.m_max_depth);
+
   logger->log ("region2 consolidation");
   logger->log ("  next region id: %i", m_next_region_id);
   log_uniq_map (logger, show_objs, "function_region2", m_fndecls_map);
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 8abf888d7f5..b71a9a41a6f 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -122,6 +122,29 @@ struct purge_stats2
   int m_num_client_items;
 };
 
+/* A measurement of the complexity of an svalue2 or region2, so that
+   we can avoid these tree-like structures growing without bounds and
+   thus leading to infinite chains of analysis.  */
+
+struct complexity
+{
+  complexity (unsigned num_nodes, unsigned max_depth)
+  : m_num_nodes (num_nodes), m_max_depth (max_depth)
+  {}
+
+  complexity (const region2 *reg);
+  complexity (const svalue2 *sval);
+  static complexity from_pair (const complexity &c1, const complexity &c2);
+
+  /* The total number of svalue2s and region2s in the tree of this
+     entity, including the entity itself.  */
+  unsigned m_num_nodes;
+
+  /* The maximum depth of the tree of this entity, including the
+     entity itself.  */
+  unsigned m_max_depth;
+};
+
 /* svalue and its subclasses.
 
    The class hierarchy looks like this (using indentation to show
@@ -220,10 +243,15 @@ public:
 			      region_model2_manager *mgr,
 			      model2_merger *merger) const;
 
+  const complexity &get_complexity () const { return m_complexity; }
+
  protected:
-  svalue2 (tree type) : m_type (type) {}
+  svalue2 (complexity c, tree type)
+  : m_complexity (c), m_type (type)
+  {}
 
  private:
+  complexity m_complexity;
   tree m_type;
 };
 
@@ -233,7 +261,9 @@ public:
 class region_svalue2 : public svalue2
 {
 public:
-  region_svalue2 (tree type, const region2 *reg) : svalue2 (type), m_reg (reg)
+  region_svalue2 (tree type, const region2 *reg)
+  : svalue2 (complexity (reg), type),
+    m_reg (reg)
   {
     gcc_assert (m_reg != NULL);
   }
@@ -287,7 +317,7 @@ class constant_svalue2 : public svalue2
 {
 public:
   constant_svalue2 (tree cst_expr)
-  : svalue2 (TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
+  : svalue2 (complexity (1, 1), TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
   {
     gcc_assert (cst_expr);
     gcc_assert (CONSTANT_CLASS_P (cst_expr));
@@ -338,7 +368,7 @@ class unknown_svalue2 : public svalue2
 {
 public:
   unknown_svalue2 (tree type)
-  : svalue2 (type)
+  : svalue2 (complexity (1, 1), type)
   {}
 
   enum kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
@@ -399,7 +429,7 @@ public:
   };
 
   poisoned_svalue2 (enum poison_kind kind, tree type)
-  : svalue2 (type), m_kind (kind) {}
+  : svalue2 (complexity (1, 1), type), m_kind (kind) {}
 
   enum kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
@@ -469,7 +499,7 @@ public:
 
   setjmp_svalue2 (const setjmp_record &setjmp_record,
 		  tree type)
-  : svalue2 (type), m_setjmp_record (setjmp_record)
+  : svalue2 (complexity (1, 1), type), m_setjmp_record (setjmp_record)
   {}
 
   enum kind get_kind () const FINAL OVERRIDE { return SK_SETJMP; }
@@ -516,7 +546,8 @@ namespace ana {
 class initial_svalue2 : public svalue2
 {
 public:
-  initial_svalue2 (tree type, const region2 *reg) : svalue2 (type), m_reg (reg)
+  initial_svalue2 (tree type, const region2 *reg)
+  : svalue2 (complexity (reg), type), m_reg (reg)
   {
     gcc_assert (m_reg != NULL);
   }
@@ -600,7 +631,7 @@ public:
   };
 
   unaryop_svalue2 (tree type, enum tree_code op, const svalue2 *arg)
-  : svalue2 (type), m_op (op), m_arg (arg)
+  : svalue2 (complexity (arg), type), m_op (op), m_arg (arg)
   {
   }
 
@@ -689,7 +720,10 @@ public:
 
   binop_svalue2 (tree type, enum tree_code op,
 		 const svalue2 *arg0, const svalue2 *arg1)
-  : svalue2 (type), m_op (op), m_arg0 (arg0), m_arg1 (arg1)
+  : svalue2 (complexity::from_pair (arg0->get_complexity (),
+				    arg1->get_complexity ()),
+	     type),
+    m_op (op), m_arg0 (arg0), m_arg1 (arg1)
   {
   }
 
@@ -778,10 +812,7 @@ public:
     const region2 *m_subregion;
   };
   sub_svalue2 (tree type, const svalue2 *parent_svalue,
-	       const region2 *subregion)
-  : svalue2 (type), m_parent_svalue (parent_svalue), m_subregion (subregion)
-  {
-  }
+	       const region2 *subregion);
 
   enum kind get_kind () const FINAL OVERRIDE { return SK_SUB; }
 
@@ -839,7 +870,7 @@ class unmergeable_svalue2 : public svalue2
 {
 public:
   unmergeable_svalue2 (const svalue2 *arg)
-    : svalue2 (arg->get_type ()), m_arg (arg)
+  : svalue2 (complexity (arg), arg->get_type ()), m_arg (arg)
   {
   }
 
@@ -883,7 +914,7 @@ class placeholder_svalue2 : public svalue2
 {
 public:
   placeholder_svalue2 (tree type, const char *name)
-  : svalue2 (type), m_name (name)
+  : svalue2 (complexity (1, 1), type), m_name (name)
   {
   }
 
@@ -1033,7 +1064,10 @@ public:
     };
   widening_svalue2 (tree type, const program_point &point,
 		    const svalue2 *base_sval, const svalue2 *iter_sval)
-  : svalue2 (type), m_point (point.get_function_point ()),
+  : svalue2 (complexity::from_pair (base_sval->get_complexity (),
+				    iter_sval->get_complexity ()),
+	     type),
+    m_point (point.get_function_point ()),
     m_base_sval (base_sval), m_iter_sval (iter_sval)
   {
   }
@@ -1136,7 +1170,8 @@ public:
   };
 
   compound_svalue2 (tree type, const binding_map &map)
-  : svalue2 (type), m_map (map)
+  : svalue2 (calc_complexity (map), type),
+    m_map (map)
   {
   }
 
@@ -1160,6 +1195,8 @@ public:
   }
 
  private:
+  static complexity calc_complexity (const binding_map &map);
+
   binding_map m_map;
 };
 
@@ -1346,8 +1383,10 @@ public:
 
   bool symbolic_for_unknown_ptr_p () const;
 
+  const complexity &get_complexity () const { return m_complexity; }
+
  protected:
-  region2 (unsigned id, const region2 *parent, tree type);
+  region2 (complexity c, unsigned id, const region2 *parent, tree type);
 
   virtual void add_to_hash (inchash::hash &hstate) const;
 
@@ -1359,6 +1398,7 @@ public:
 #endif
   region_offset calc_offset () const;
 
+  complexity m_complexity;
   unsigned m_id; // purely for deterministic sorting at this stage, for dumps
   const region2 *m_parent;
   tree m_type;
@@ -1385,7 +1425,7 @@ class space_region2 : public region2
 {
 protected:
   space_region2 (unsigned id, const region2 *parent)
-  : region2 (id, parent, NULL_TREE)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
   {}
 };
 
@@ -1527,7 +1567,8 @@ class function_region2 : public region2
 {
 public:
   function_region2 (unsigned id, const code_region2 *parent, tree fndecl)
-  : region2 (id, parent, TREE_TYPE (fndecl)), m_fndecl (fndecl)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (fndecl)),
+    m_fndecl (fndecl)
   {
     gcc_assert (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (fndecl)));
   }
@@ -1570,7 +1611,7 @@ class label_region2 : public region2
 {
 public:
   label_region2 (unsigned id, const function_region2 *parent, tree label)
-  : region2 (id, parent, NULL_TREE), m_label (label)
+  : region2 (complexity (parent), id, parent, NULL_TREE), m_label (label)
   {
     gcc_assert (TREE_CODE (label) == LABEL_DECL);
   }
@@ -1605,7 +1646,7 @@ class stack_region2 : public region2
 {
 public:
   stack_region2 (unsigned id, region2 *parent)
-  : region2 (id, parent, NULL_TREE)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
   {}
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
@@ -1650,7 +1691,7 @@ class heap_region2 : public region2
 {
 public:
   heap_region2 (unsigned id, region2 *parent)
-    : region2 (id, parent, NULL_TREE)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
   {}
 
 #if 0
@@ -1732,7 +1773,8 @@ class symbolic_region2 : public region2
 {
 public:
   symbolic_region2 (unsigned id, region2 *parent, const svalue2 *sval_ptr)
-  : region2 (id, parent, TREE_TYPE (sval_ptr->get_type ())),
+  : region2 (complexity::from_pair (parent, sval_ptr), id, parent,
+	     TREE_TYPE (sval_ptr->get_type ())),
     m_sval_ptr (sval_ptr)
   {}
 
@@ -1769,7 +1811,7 @@ class decl_region2 : public region2
 {
 public:
   decl_region2 (unsigned id, const region2 *parent, tree decl)
-  : region2 (id, parent, TREE_TYPE (decl)), m_decl (decl)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (decl)), m_decl (decl)
   {}
 
   bool compare_fields (const decl_region2 &other) const;
@@ -1808,7 +1850,8 @@ class field_region2 : public region2
 {
 public:
   field_region2 (unsigned id, const region2 *parent, tree field)
-  : region2 (id, parent, TREE_TYPE (field)), m_field (field)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (field)),
+    m_field (field)
   {}
 
   bool compare_fields (const field_region2 &other) const;
@@ -1847,7 +1890,8 @@ class element_region2 : public region2
 public:
   element_region2 (unsigned id, const region2 *parent, tree element_type,
 		   const svalue2 *index)
-  : region2 (id, parent, element_type), m_index (index)
+  : region2 (complexity::from_pair (parent, index), id, parent, element_type),
+    m_index (index)
   {}
 
   bool compare_fields (const element_region2 &other) const;
@@ -1887,7 +1931,8 @@ class offset_region2 : public region2
 public:
   offset_region2 (unsigned id, const region2 *parent, tree type,
 		  const svalue2 *byte_offset)
-  : region2 (id, parent, type), m_byte_offset (byte_offset)
+  : region2 (complexity::from_pair (parent, byte_offset), id, parent, type),
+    m_byte_offset (byte_offset)
   {}
 
   bool compare_fields (const offset_region2 &other) const;
@@ -1925,7 +1970,8 @@ class cast_region2 : public region2
 {
 public:
   cast_region2 (unsigned id, const region2 *original_region, tree type)
-  : region2 (id, original_region->get_parent_region (), type),
+  : region2 (complexity (original_region), id,
+	     original_region->get_parent_region (), type),
     m_original_region (original_region)
   {}
 
@@ -1967,7 +2013,7 @@ class heap_allocated_region2 : public region2
 {
 public:
   heap_allocated_region2 (unsigned id, const region2 *parent)
-  : region2 (id, parent, NULL_TREE)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
   {}
 
 #if 0
@@ -1989,7 +2035,7 @@ class alloca_region2 : public region2
 {
 public:
   alloca_region2 (unsigned id, const frame_region2 *parent)
-  : region2 (id, parent, NULL_TREE)
+  : region2 (complexity (parent), id, parent, NULL_TREE)
   {}
 
 #if 0
@@ -2011,7 +2057,8 @@ class string_region2 : public region2
 {
 public:
   string_region2 (unsigned id, const region2 *parent, tree string_cst)
-  : region2 (id, parent, TREE_TYPE (string_cst)), m_string_cst (string_cst)
+  : region2 (complexity (parent), id, parent, TREE_TYPE (string_cst)),
+    m_string_cst (string_cst)
   {}
 
   const string_region2 *
@@ -2052,7 +2099,7 @@ class unknown_region2 : public region2
 {
 public:
   unknown_region2 (unsigned id, const region2 *parent, tree type)
-  : region2 (id, parent, type)
+  : region2 (complexity (parent), id, parent, type)
   {}
 
 #if 0
@@ -2148,6 +2195,9 @@ public:
   void log_stats (logger *logger, bool show_objs) const;
 
 private:
+  bool too_complex_p (const complexity &c) const;
+  bool reject_if_too_complex (svalue2 *sval);
+
   const svalue2 *maybe_fold_unaryop (tree type, enum tree_code op,
 				     const svalue2 *arg);
   const svalue2 *maybe_fold_binop (tree type, enum tree_code op,
@@ -2207,6 +2257,9 @@ private:
 		   compound_svalue2 *> compound_values_map_t;
   compound_values_map_t m_compound_values_map;
 
+  /* Maximum complexity of svalues that weren't rejected.  */
+  complexity m_max_complexity;
+
   /* region2 consolidation.  */
 
   code_region2 m_code_region;
diff --git a/gcc/testsuite/gcc.dg/analyzer/use-after-free.c b/gcc/testsuite/gcc.dg/analyzer/use-after-free.c
new file mode 100644
index 00000000000..d7e4bc2c6ca
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/use-after-free.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+struct link { struct link *next; };
+
+int free_a_list_badly (struct link *n)
+{
+  while (n) {
+    free(n); /* { dg-message "freed here" } */
+    n = n->next; /* { dg-warning "use after 'free' of 'n'" } */
+  }
+}
-- 
2.26.2

