In one of my recent patches, I delete the sym->common_head to plug a
memory leak.
However, for invalid commons, gfc_undo_symbols is called, which might
remove the common head while it is still referenced from
namespace->common_root, leading to invalid memory access and (on some
systems) to ICEs (segfaults).
In my memory leak patch, unused common_heads are deleted. However,
before with undo_symbols the symtree in wasn't deleted. This lead to
access to invalid memory for invalid programs - either visible via
valgrind or as ICE (segfault [e.g. for gfortran.dg/common_6.f90]).
Build and regtested on x86-64-linux. I also checked
gfortran.dg/common_6.f90 for invalid memory access with valgrind and the
Polyhedron test cases for memory leaks [there are no new onces].
I intent to commit the attached patch tomorrow as obvious.
Tobias
2012-09-01 Tobias Burnus <bur...@net-b.de>
PR fortran/54426
* symbol.c (find_common_symtree): New function.
(gfc_undo_symbols): Use it; free common_head if needed.
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
index 5e97c40..8d3b56c 100644
--- a/gcc/fortran/symbol.c
+++ b/gcc/fortran/symbol.c
@@ -2867,6 +2867,30 @@ gfc_get_ha_symbol (const char *name, gfc_symbol **result)
return i;
}
+
+/* Search for the symtree belonging to a gfc_common_head; we cannot use
+ head->name as the common_root symtree's name might be mangled. */
+
+static gfc_symtree *
+find_common_symtree (gfc_symtree *st, gfc_common_head *head)
+{
+
+ gfc_symtree *result;
+
+ if (st == NULL)
+ return NULL;
+
+ if (st->n.common == head)
+ return st;
+
+ result = find_common_symtree (st->left, head);
+ if (!result)
+ result = find_common_symtree (st->right, head);
+
+ return result;
+}
+
+
/* Undoes all the changes made to symbols in the current statement.
This subroutine is made simpler due to the fact that attributes are
never removed once added. */
@@ -2890,6 +2914,17 @@ gfc_undo_symbols (void)
needs to be removed to stop the resolver looking
for a (possibly) dead symbol. */
+ if (p->common_block->head == p && !p->common_next)
+ {
+ gfc_symtree st, *st0;
+ st0 = find_common_symtree (p->ns->common_root,
+ p->common_block);
+
+ st.name = st0->name;
+ gfc_delete_bbt (&p->ns->common_root, &st, compare_symtree);
+ free (st0);
+ }
+
if (p->common_block->head == p)
p->common_block->head = p->common_next;
else