|
From 5550a28098602b8fe0a68ac2529ffe2695beb589 Mon Sep 17 00:00:00 2001
|
|
From: Dylan Thacker-Smith <Dylan.Smith@shopify.com>
|
|
Date: Fri, 23 Aug 2019 15:38:06 -0400
|
|
Subject: [PATCH 2/4] Fix freeing and clearing destination hash in
|
|
Hash#initialize_copy
|
|
|
|
The code was assuming the state of the destination hash based on the
|
|
source hash for clearing any existing table on it. If these don't match,
|
|
then that can cause the old table to be leaked. This can be seen by
|
|
compiling hash.c with `#define HASH_DEBUG 1` and running the following
|
|
script, which will crash from a debug assertion.
|
|
|
|
```ruby
|
|
h = 9.times.map { |i| [i, i] }.to_h
|
|
h.send(:initialize_copy, {})
|
|
```
|
|
---
|
|
hash.c | 10 ++++++++--
|
|
1 file changed, 8 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/hash.c b/hash.c
|
|
index f91958ff0f..ee5dc0e004 100644
|
|
--- a/hash.c
|
|
+++ b/hash.c
|
|
@@ -2792,14 +2792,20 @@ rb_hash_initialize_copy(VALUE hash, VALUE hash2)
|
|
|
|
if (hash == hash2) return hash;
|
|
|
|
+ if (RHASH_ST_TABLE_P(hash)) {
|
|
+ st_free_table(RHASH_ST_TABLE(hash));
|
|
+ RHASH_ST_CLEAR(hash);
|
|
+ }
|
|
+ else {
|
|
+ ar_free_and_clear_table(hash);
|
|
+ }
|
|
+
|
|
if (RHASH_AR_TABLE_P(hash2)) {
|
|
- if (RHASH_AR_TABLE_P(hash)) ar_free_and_clear_table(hash);
|
|
ar_copy(hash, hash2);
|
|
if (RHASH_AR_TABLE_SIZE(hash))
|
|
rb_hash_rehash(hash);
|
|
}
|
|
else if (RHASH_ST_TABLE_P(hash2)) {
|
|
- if (RHASH_ST_TABLE_P(hash)) st_free_table(RHASH_ST_TABLE(hash));
|
|
RHASH_ST_TABLE_SET(hash, st_copy(RHASH_ST_TABLE(hash2)));
|
|
if (RHASH_ST_TABLE(hash)->num_entries)
|
|
rb_hash_rehash(hash);
|
|
--
|
|
2.21.0
|
|
|