From 365c151df0a8bd495328e012e0a27a8585b10355 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sun, 15 Jan 2023 18:16:42 +0900 Subject: [PATCH] Make `mrb_env_unshare()` call in `mrb_vm_run()` Move the detachment of the "env" object, now done by `mrb_top_run()` by #5904, to `mrb_vm_run()`. This is because `mrb_vm_run()` can remove `mrb_env_unshare()` which is called from `bin/mirb`. Also, even if the mruby VM is already running, either of the following conditions should be used to detach "env": - If the `stack_keep` variable is 0. - If the stack length of "env" is longer than `irep->nlocals`. The reason for the change is that the stack beyond `irep->nlocals` is used inside the called method, and previously it was possible to reference and manipulate the state inside the method via "env". env stack | | main stack | top | m1 | m2 | |<--------------->| operable via env stack (including self) If the problematic block is called from the `m2` method above, it is possible to replace `self` in `m1` and `m2` as well as the internal variables. This change may cause compatibility problems, but I believe it is better to make `MRB_API`, `mrb_vm_run()` safe. If a dangerous procedure is absolutely necessary, `mrb_vm_exec()` can still be called as before. --- src/vm.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vm.c b/src/vm.c index 2328497fa..6c7b81b4a 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1269,6 +1269,13 @@ mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int sta } if (stack_keep > nregs) nregs = stack_keep; + else { + struct REnv *e = CI_ENV(mrb->c->ci); + if (stack_keep == 0 || (e && irep->nlocals < MRB_ENV_LEN(e))) { + mrb_vm_ci_env_set(mrb->c->ci, NULL); + mrb_env_unshare(mrb, e, FALSE); + } + } mrb_stack_extend(mrb, nregs); stack_clear(c->ci->stack + stack_keep, nregs - stack_keep); c->ci->stack[0] = self; @@ -3096,11 +3103,6 @@ mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int st return mrb_vm_run(mrb, proc, self, stack_keep); } if (mrb->c->ci == mrb->c->cibase) { - if (stack_keep == 0) { - struct REnv *e = mrb_vm_ci_env(mrb->c->ci); - mrb_vm_ci_env_set(mrb->c->ci, NULL); - mrb_env_unshare(mrb, e, FALSE); - } return mrb_vm_run(mrb, proc, self, stack_keep); } cipush(mrb, 0, CINFO_SKIP, mrb->object_class, NULL, NULL, 0, 0);