From 1ae74499395de2b8c2170d20f775ae0032e8cfb9 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 23 Jan 2015 19:07:14 +0100 Subject: [PATCH] gc: Do not finish lazy sweeps when freeing the object space When freeing the object space from `ruby_vm_destruct`, we were performing a `gc_rest_sweep` to wrap up any potentially undergoing lazy sweeps. This operation is extremely dangerous because it is performed without a GVL (which has been removed previously in `ruby_vm_destruct`) and without a main thread to operate with (which has been cleared previously when freeing the main VM thread in `ruby_vm_destruct`). Since finishing a lazy sweep on the heap can cause objects to be deallocated, if the free function of any of these objects uses threading information, it will segfault. The builtin `Fiber` object (cont.c), for instance, accesses current thread information with `GET_THREAD()` when being freed, and can potentially segfault. Any user defined T_DATA with a free callback can also trigger segfaults, and likewise for any object in Rubyland with a finalizer. Because of the way that the MRI teardown process functions, the only way that a lazy sweep can be underway when freeing the object space is if it was triggered when calling the on-exit finalizers (`rb_gc_call_finalizer_at_exit`). Before calling all the object finalizers, `rb_objspace_call_finalizer` finishes any current lazy sweeps. However, since the process of calling finalizers can allocate more objects on the Ruby heap, it is possible for a call to `newobj` to start another lazy sweep phase. Hence, instead of finishing the lazy sweep when freeing the object space, we attempt to finish it right after all the finalizers have been called. This way, we can assert that no lazy sweeps and no object deallocation will be performed after the main thread and the GVL have been cleared. --- gc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gc.c b/gc.c index 7fa70c2c7a7832..d8fb2f4db6900e 100644 --- a/gc.c +++ b/gc.c @@ -920,7 +920,8 @@ static void heap_page_free(rb_objspace_t *objspace, struct heap_page *page); void rb_objspace_free(rb_objspace_t *objspace) { - gc_rest_sweep(objspace); + if (is_lazy_sweeping(heap_eden)) + rb_bug("lazy sweeping underway when freeing object space"); if (objspace->profile.records) { free(objspace->profile.records); @@ -2255,6 +2256,12 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace) st_free_table(finalizer_table); finalizer_table = 0; ATOMIC_SET(finalizing, 0); + + /* + * finish any lazy sweeps that may have been started + * when finalizing the objects in the heap + */ + gc_rest_sweep(objspace); } static inline int