Project

General

Profile

Bug #16121 » 0002-Fix-freeing-and-clearing-destination-hash-in-Hash.diff.txt

[PATCH 2/4] Fix freeing and clearing destination hash in Hash#initialize_copy - dylants (Dylan Thacker-Smith), 08/23/2019 07:55 PM

 
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

(2-2/4)