[Forttran] PR54426 - handle common_head in gfc_undo_symbols

Message ID 5042739F.3030900@net-b.de
State New
Headers show

Commit Message

Tobias Burnus Sept. 1, 2012, 8:44 p.m.
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.



2012-09-01  Tobias Burnus  <burnus@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;