summaryrefslogtreecommitdiff
path: root/hash.c
diff options
context:
space:
mode:
authorPeter Zhu <[email protected]>2023-05-23 13:45:44 -0400
committerPeter Zhu <[email protected]>2023-05-23 15:27:56 -0400
commita86f798fc2e979ea83ec55744f906b2a816c8681 (patch)
tree897aff087429971d6e7532572a42e46a759338fa /hash.c
parent061e01ee5088b491ab702a567eb0d5bed1c5d429 (diff)
Fix crash when replacing ST hash with AR hash
With VWA, AR hashes are much larger than ST hashes. Hash#replace attempts to directly copy the contents of AR hashes into ST hashes so there will be memory corruption caused by writing past the end of memory. This commit changes it so that if a ST hash is being replaced with an AR hash it will insert each element into the ST hash.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/7846
Diffstat (limited to 'hash.c')
-rw-r--r--hash.c30
1 files changed, 27 insertions, 3 deletions
diff --git a/hash.c b/hash.c
index 9b6d9b9aea..80ed64527d 100644
--- a/hash.c
+++ b/hash.c
@@ -2906,14 +2906,38 @@ rb_hash_replace(VALUE hash, VALUE hash2)
else {
RHASH_ST_CLEAR(hash);
}
- hash_copy(hash, hash2);
+
+ if (RHASH_AR_TABLE_P(hash2)) {
+ if (RHASH_AR_TABLE_P(hash)) {
+ ar_copy(hash, hash2);
+ }
+ else {
+ st_table *tab = RHASH_ST_TABLE(hash);
+ rb_st_init_existing_table_with_size(tab, &objhash, RHASH_AR_TABLE_SIZE(hash2));
+
+ int bound = RHASH_AR_TABLE_BOUND(hash2);
+ for (int i = 0; i < bound; i++) {
+ if (ar_cleared_entry(hash2, i)) continue;
+
+ ar_table_pair *pair = RHASH_AR_TABLE_REF(hash2, i);
+ st_add_direct(tab, pair->key, pair->val);
+ RB_OBJ_WRITTEN(hash, Qundef, pair->key);
+ RB_OBJ_WRITTEN(hash, Qundef, pair->val);
+ }
+ }
+ }
+ else {
+ HASH_ASSERT(sizeof(st_table) <= sizeof(ar_table));
+
+ RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(hash2)));
+ rb_gc_writebarrier_remember(hash);
+ }
+
if (RHASH_EMPTY_P(hash2) && RHASH_ST_TABLE_P(hash2)) {
/* ident hash */
hash_st_table_init(hash, RHASH_TYPE(hash2), 0);
}
- rb_gc_writebarrier_remember(hash);
-
return hash;
}