From 85092ecd6f5c4d12d0cb1d6dfa7040337a4f558b Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 29 Nov 2023 18:10:13 -0500 Subject: [PATCH] Fix imemo_env corruption under auto compaction Previously, vm_make_env_each() did: 1. ALLOC env_body 2. Copy locals into env_body 3. Allocate imemo_env 4. Set up imemo_env with env_body If compaction runs during (3), locals copied to env_body could be moved and the imemo_env could end up with invalid references. Move (2) down so it reads references after potential movement. --- vm.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/vm.c b/vm.c index 051e66c010..74f6cc2971 100644 --- a/vm.c +++ b/vm.c @@ -918,8 +918,6 @@ static VALUE vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *const cfp) { const VALUE * const ep = cfp->ep; - const rb_env_t *env; - const rb_iseq_t *env_iseq; VALUE *env_body, *env_ep; int local_size, env_size; @@ -971,9 +969,26 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co env_size = local_size + 1 /* envval */; + + // Careful with order in the following sequence. Each allocation can move objects. env_body = ALLOC_N(VALUE, env_size); + rb_env_t *env = (rb_env_t *)rb_imemo_new(imemo_env, 0, 0, 0, 0); + + // Set up env without WB since it's brand new (similar to newobj_init(), newobj_fill()) MEMCPY(env_body, ep - (local_size - 1 /* specval */), VALUE, local_size); + env_ep = &env_body[local_size - 1 /* specval */]; + env_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)env; + + env->iseq = (rb_iseq_t *)(VM_FRAME_RUBYFRAME_P(cfp) ? cfp->iseq : NULL); + env->ep = env_ep; + env->env = env_body; + env->env_size = env_size; + + cfp->ep = env_ep; + VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED); + VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */ + #if 0 for (i = 0; i < local_size; i++) { if (VM_FRAME_RUBYFRAME_P(cfp)) { @@ -983,14 +998,6 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co } #endif - env_iseq = VM_FRAME_RUBYFRAME_P(cfp) ? cfp->iseq : NULL; - env_ep = &env_body[local_size - 1 /* specval */]; - - env = vm_env_new(env_ep, env_body, env_size, env_iseq); - - cfp->ep = env_ep; - VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED); - VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */ return (VALUE)env; }