diff options
author | Peter Zhu <peter@peterzhu.ca> | 2023-12-04 14:00:00 -0500 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2023-12-05 08:42:25 -0500 |
commit | ed25f0bd5a4fb936eddde080b90446e7d55afb2d (patch) | |
tree | 445575d4c3d315df871e3213f43902096576206d | |
parent | bf0c8055ab29a9c2d8280028379c4a7cb033905e (diff) | |
download | ruby-ed25f0bd5a4fb936eddde080b90446e7d55afb2d.tar.gz |
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.
-rw-r--r-- | proc.c | 8 |
1 files changed, 7 insertions, 1 deletions
@@ -3419,9 +3419,15 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref) } 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_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); VM_ASSERT(VM_ENV_ESCAPED_P(new_ep)); return new_env; |