-
dearblue authored
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
dearblue authoredCalling `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
Loading