rb_unlink_method_entry may cause old_me to be swept before the new
method is inserted; allowing for a window where the method entry may
be prematurely freed/swept but still referenced in the old slot of
the method entry table.

From what I can tell, this is not currently a problem with when using
st.  However, with my proposed ihash method table implementation, this
caused a segfault with the test[1] for bug #8100 where the method entry
is itself a linked list node, there is no st_table_entry.

Right now, this patch will make us more robust against future changes.

[1] test/ruby/test_method.rb:
  test_unlinked_method_entry_in_method_object_bug
---
 Thoughts?  I will likely commit this separately since changing the
 method table implementation may not happen soon.

 vm_method.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/vm_method.c b/vm_method.c
index d551332..beb9008 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -255,6 +255,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
     st_table *mtbl;
     st_data_t data;
     int make_refined = 0;
+    rb_method_entry_t *old_me;
 
     if (NIL_P(klass)) {
 	klass = rb_cObject;
@@ -279,8 +280,7 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
 	rb_add_refined_method_entry(refined_class, mid);
     }
     if (type == VM_METHOD_TYPE_REFINED) {
-	rb_method_entry_t *old_me =
-	    lookup_method_table(RCLASS_ORIGIN(klass), mid);
+	old_me = lookup_method_table(RCLASS_ORIGIN(klass), mid);
 	if (old_me) rb_vm_check_redefinition_opt_method(old_me, klass);
     }
     else {
@@ -289,9 +289,12 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
     mtbl = RCLASS_M_TBL(klass);
 
     /* check re-definition */
+    old_me = 0;
     if (st_lookup(mtbl, mid, &data)) {
-	rb_method_entry_t *old_me = (rb_method_entry_t *)data;
-	rb_method_definition_t *old_def = old_me->def;
+	rb_method_definition_t *old_def;
+
+	old_me = (rb_method_entry_t *)data;
+	old_def = old_me->def;
 
 	if (rb_method_definition_eq(old_def, def)) return old_me;
 #if NOEX_NOREDEF
@@ -329,8 +332,6 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
 				   rb_id2name(old_def->original_id));
 	    }
 	}
-
-	rb_unlink_method_entry(old_me);
     }
 
     mid = SYM2ID(ID2SYM(mid));
@@ -379,6 +380,9 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type,
     }
 
     st_insert(mtbl, mid, (st_data_t) me);
+    if (old_me) {
+	rb_unlink_method_entry(old_me);
+    }
 
     return me;
 }
-- 
Eric Wong