From 27c025429927c92daf64c04e0738591c35921457 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 15 Jul 2020 15:59:03 -0400 Subject: [PATCH 305/315] 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 +#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