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.
This commit is contained in:
dearblue
2023-01-15 18:16:42 +09:00
parent a96a9f2c2b
commit 365c151df0
+7 -5
View File
@@ -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);