Fix use-after-free by mrb_gc_unregistor()

Calling `mrb_gc_unregistor()` from `mrb_data_type::dfree` caused a use-after-free deep inside `mrb_close()`.
The impetus to investigate was <https://github.com/mruby/mruby/pull/6342#pullrequestreview-2292747530>.

Currently, when `mrb_close()` is called, all objects are destroyed first.
The process is done heap page by heap page, and when all objects belonging to a heap page are destroyed, the heap page is released.
If the next heap page contains `RData` objects, the `mrb_gc_unregistor()` function may be called from the `mrb_data_type::dfree` function.
At this time, the `mrb_gc_unregistor()` function gets an array object from a Ruby global variable.
If the array object belongs to a freed heap page, use-after-free is established by referencing this array object.

About the fixes.

First of all, there is the fact that the `mrb_gv_get()` function returns `nil` if `mrb->globals` is `NULL`.
Therefore, before destroying all objects, free `mrb->globals` and set `mrb->globals` to `NULL` at the same time.
Now the `mrb_gv_get()` function will return `nil` to the calling `mrb_gc_unregistor()` function and `mrb_gc_unregistor()` will do nothing more.

ref. https://github.com/mruby/mruby/issues/4618
This commit is contained in:
dearblue
2024-10-22 22:57:49 +09:00
parent b95ca53e5c
commit be9431a9ef
2 changed files with 4 additions and 2 deletions
+1 -1
View File
@@ -185,9 +185,9 @@ mrb_close(mrb_state *mrb)
mrb_protect_atexit(mrb);
/* free */
mrb_gc_free_gv(mrb);
mrb_gc_destroy(mrb, &mrb->gc);
mrb_free_context(mrb, mrb->root_c);
mrb_gc_free_gv(mrb);
mrb_free_symtbl(mrb);
mrb_free(mrb, mrb);
}
+3 -1
View File
@@ -251,8 +251,10 @@ mrb_gc_mark_gv(mrb_state *mrb)
void
mrb_gc_free_gv(mrb_state *mrb)
{
if (mrb->globals)
if (mrb->globals) {
iv_free(mrb, mrb->globals);
mrb->globals = NULL;
}
}
size_t