aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-12-04 14:00:00 -0500
committerPeter Zhu <peter@peterzhu.ca>2023-12-05 08:42:25 -0500
commited25f0bd5a4fb936eddde080b90446e7d55afb2d (patch)
tree445575d4c3d315df871e3213f43902096576206d
parentbf0c8055ab29a9c2d8280028379c4a7cb033905e (diff)
downloadruby-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.c8
1 files changed, 7 insertions, 1 deletions
diff --git a/proc.c b/proc.c
index 475879f9d5..c770e79880 100644
--- a/proc.c
+++ b/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);
- 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;