From cf23861166cf7811ee7f3aef2987500acab05231 Mon Sep 17 00:00:00 2001 From: dearblue Date: Thu, 19 Sep 2024 23:13:18 +0900 Subject: [PATCH] Making splat argument objects invisible from Ruby side The `mrb_get_argv()` function and the `*` specifier of `mrb_get_args()` get the address of the argument. At this time, if it is passed in the form of a splat argument, it will be an address to an element of an array object. After getting the pointer to the array object, the caller may call `mrb_vm_exec()` directly or indirectly. At this time, a splat argument with the class set can be retrieved as an array object by searching with `ObjectSpace.each_object`. If changes are made as array objects, addresses on the heap as arrays may become invalid, or objects in the array may be recycled by the GC. When the caller references the changed address in a subsequent operation, use-after-free is established. This patch assigns `NULL` as the class of the array object so that it cannot be detected by `ObjectSpace.each_object` from the Ruby side. --- src/class.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/class.c b/src/class.c index f0008a164..8b5eee766 100644 --- a/src/class.c +++ b/src/class.c @@ -834,6 +834,7 @@ mrb_get_argc(mrb_state *mrb) if (argc == 15) { struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]); + a->c = NULL; /* hide from ObjectSpace.each_object */ argc = ARY_LEN(a); } return argc; @@ -847,6 +848,7 @@ mrb_get_argv(mrb_state *mrb) if (argc == 15) { struct RArray *a = mrb_ary_ptr(*array_argv); + a->c = NULL; /* hide from ObjectSpace.each_object */ array_argv = ARY_PTR(a); } return array_argv; @@ -965,6 +967,7 @@ get_args_v(mrb_state *mrb, mrb_args_format format, void** ptr, va_list *ap) struct RArray *a = mrb_ary_ptr(*argv); argv = ARY_PTR(a); argc = ARY_LEN(a); + a->c = NULL; /* hide from ObjectSpace.each_object */ } opt = FALSE;