https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92761

            Bug ID: 92761
           Summary: hash_table::expand invokes assignment on invalid
                    objects
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: bootstrap
          Assignee: unassigned at gcc dot gnu.org
          Reporter: msebor at gcc dot gnu.org
  Target Milestone: ---

The GCC internal hash_table::expand() function invokes the assignment operator
on invalid/empty elements.  The following test case inserted into the C++
parser (where I ran into the problem) reproduces the bug:

struct S
{
  S (): p (&p) { }
  S (const S &s): p (&p) { gcc_assert (s.p == &s.p); }
  S& operator= (const S &s) { gcc_assert (s.p == &s.p); return *this; }
  ~S () { gcc_assert (p == &p); }
  void *p;
};

hash_map<tree, S> x;

static void test_hash_table ()
{
  for (int i = 0; i != 32; ++i)
    x.put ((tree)i, S ());

  x.empty ();
}

/* Parse one entire translation unit.  */

void
c_parse_file (void)
{
  test_hash_table ();
  ...
}

internal compiler error: in operator=, at cp/parser.c:43418
0xaeed4f S::operator=(S const&)
        /src/gcc/61339/gcc/cp/parser.c:43418
0xaf3a7e hash_map<tree_node*, S,
simple_hashmap_traits<default_hash_traits<tree_node*>, S>
>::hash_entry::operator=(hash_map<tree_node*, S,
simple_hashmap_traits<default_hash_traits<tree_node*>, S> >::hash_entry const&)
        /src/gcc/61339/gcc/hash-map.h:42
0xaf3c24 hash_table<hash_map<tree_node*, S,
simple_hashmap_traits<default_hash_traits<tree_node*>, S> >::hash_entry, false,
xcallocator>::expand()
        /src/gcc/61339/gcc/hash-table.h:822
0xaf21a7 hash_table<hash_map<tree_node*, S,
simple_hashmap_traits<default_hash_traits<tree_node*>, S> >::hash_entry, false,
xcallocator>::find_slot_with_hash(tree_node* const&, unsigned int,
insert_option)
        /src/gcc/61339/gcc/hash-table.h:962
0xaf02e4 hash_map<tree_node*, S,
simple_hashmap_traits<default_hash_traits<tree_node*>, S> >::put(tree_node*
const&, S const&)
        /src/gcc/61339/gcc/hash-map.h:165
0xaea3b8 test_hash_table
        /src/gcc/61339/gcc/cp/parser.c:43428
0xaea3e5 c_parse_file()
        /src/gcc/61339/gcc/cp/parser.c:43438
0xcd34ab c_common_parse_file()
        /src/gcc/61339/gcc/c-family/c-opts.c:1185
Please submit a full bug report,


As the stack trace shows, the problem is due to expand() directly assigning to
the (invalid) lvalue obtained from find_empty_slot_for_expand:

template<typename Descriptor, bool Lazy,
         template<typename Type> class Allocator>
void
hash_table<Descriptor, Lazy, Allocator>::expand ()
{
  value_type *oentries = m_entries;
  ...
  value_type *p = oentries;
  do
    {
      value_type &x = *p;

      if (!is_empty (x) && !is_deleted (x))
        {
          value_type *q = find_empty_slot_for_expand (Descriptor::hash (x));

          *q = x;   <<< q points to a empty slot with no object in it
        }

      p++;
    }
  while (p < olimit);
  ...
}

Reply via email to