Make env_clone compaction safe

The original order of events is:

1. Allocate new_body.
2. Peform memcpy into new_body.
3. Create new_env using new_body.

However, if GC compaction runs during step 3, then new_env would not
have yet been created and objects on new_body could move but it would
not be reference updated.

This commit changes the order of the last two events.
This commit is contained in:
Peter Zhu 2023-12-04 14:00:00 -05:00
Родитель bf0c8055ab
Коммит ed25f0bd5a
1 изменённых файлов: 7 добавлений и 1 удалений

8
proc.c
Просмотреть файл

@ -3419,9 +3419,15 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref)
} }
new_body = ALLOC_N(VALUE, env->env_size); new_body = ALLOC_N(VALUE, env->env_size);
MEMCPY(new_body, env->env, VALUE, env->env_size);
new_ep = &new_body[env->ep - env->env]; new_ep = &new_body[env->ep - env->env];
new_env = vm_env_new(new_ep, new_body, env->env_size, env->iseq); new_env = vm_env_new(new_ep, new_body, env->env_size, env->iseq);
/* The memcpy has to happen after the vm_env_new because it can trigger a
* GC compaction which can move the objects in the env. */
MEMCPY(new_body, env->env, VALUE, env->env_size);
/* VM_ENV_DATA_INDEX_ENV is set in vm_env_new but will get overwritten
* by the memcpy above. */
new_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)new_env;
RB_OBJ_WRITE(new_env, &new_ep[VM_ENV_DATA_INDEX_ME_CREF], (VALUE)cref); RB_OBJ_WRITE(new_env, &new_ep[VM_ENV_DATA_INDEX_ME_CREF], (VALUE)cref);
VM_ASSERT(VM_ENV_ESCAPED_P(new_ep)); VM_ASSERT(VM_ENV_ESCAPED_P(new_ep));
return new_env; return new_env;