Fix memory leak in Variable Width Allocation

Force recycled objects could create a freelist for the page. At the
start of sweeping we should append to the freelist to avoid
permanently losing the slots on the freelist.
This commit is contained in:
Peter Zhu 2021-08-27 09:37:51 -04:00
Родитель 3562c7ba33
Коммит ed31bdfeee
1 изменённых файлов: 27 добавлений и 23 удалений

50
gc.c
Просмотреть файл

@ -5534,6 +5534,30 @@ gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode)
gc_mode_set(objspace, mode); gc_mode_set(objspace, mode);
} }
static void
heap_page_freelist_append(struct heap_page *page, RVALUE *freelist)
{
if (freelist) {
asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
if (page->freelist) {
RVALUE *p = page->freelist;
asan_unpoison_object((VALUE)p, false);
while (p->as.free.next) {
RVALUE *prev = p;
p = p->as.free.next;
asan_poison_object((VALUE)prev);
asan_unpoison_object((VALUE)p, false);
}
p->as.free.next = freelist;
asan_poison_object((VALUE)p);
}
else {
page->freelist = freelist;
}
asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
}
}
static void static void
gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap) gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap)
{ {
@ -5560,12 +5584,10 @@ gc_sweep_start(rb_objspace_t *objspace)
rb_size_pool_t *size_pool = &size_pools[i]; rb_size_pool_t *size_pool = &size_pools[i];
#if USE_RVARGC #if USE_RVARGC
if (size_pool->freelist) { heap_page_freelist_append(size_pool->using_page, size_pool->freelist);
size_pool->using_page->freelist = (RVALUE *)size_pool->freelist;
size_pool->freelist = 0;
}
size_pool->using_page = NULL; size_pool->using_page = NULL;
size_pool->freelist = NULL;
#endif #endif
gc_sweep_start_heap(objspace, SIZE_POOL_EDEN_HEAP(size_pool)); gc_sweep_start_heap(objspace, SIZE_POOL_EDEN_HEAP(size_pool));
@ -8713,25 +8735,7 @@ rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache)
RVALUE *freelist = newobj_cache->freelist; RVALUE *freelist = newobj_cache->freelist;
RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", page, freelist); RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", page, freelist);
if (page && freelist) { heap_page_freelist_append(page, freelist);
asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
if (page->freelist) {
RVALUE *p = page->freelist;
asan_unpoison_object((VALUE)p, false);
while (p->as.free.next) {
RVALUE *prev = p;
p = p->as.free.next;
asan_poison_object((VALUE)prev);
asan_unpoison_object((VALUE)p, false);
}
p->as.free.next = freelist;
asan_poison_object((VALUE)p);
}
else {
page->freelist = freelist;
}
asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
}
newobj_cache->using_page = NULL; newobj_cache->using_page = NULL;
newobj_cache->freelist = NULL; newobj_cache->freelist = NULL;