diff options
author | Peter Zhu <peter@peterzhu.ca> | 2023-06-29 09:21:11 -0400 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2023-06-29 11:16:50 -0400 |
commit | f0d08d11dcd404f3146c0d71d6ff743bbc6e7193 (patch) | |
tree | 82e6bb0fff741e124915c7a2f6aabe54a484ee71 /st.c | |
parent | df2b3a29987e9353596af76ed77f35d7366d654e (diff) | |
download | ruby-f0d08d11dcd404f3146c0d71d6ff743bbc6e7193.tar.gz |
Fix memory leak when copying ST tables
st_copy allocates a st_table, which is not needed for hashes since it is
allocated by VWA and embedded, so this causes a memory leak.
The following script demonstrates the issue:
```ruby
20.times do
100_000.times do
{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9}
end
puts `ps -o rss= -p #{$$}`
end
```
Diffstat (limited to 'st.c')
-rw-r--r-- | st.c | 34 |
1 files changed, 23 insertions, 11 deletions
@@ -1228,17 +1228,10 @@ st_insert2(st_table *tab, st_data_t key, st_data_t value, return 1; } -/* Create and return a copy of table OLD_TAB. */ +/* Create a copy of old_tab into new_tab. */ st_table * -st_copy(st_table *old_tab) +st_replace(st_table *new_tab, st_table *old_tab) { - st_table *new_tab; - - new_tab = (st_table *) malloc(sizeof(st_table)); -#ifndef RUBY - if (new_tab == NULL) - return NULL; -#endif *new_tab = *old_tab; if (old_tab->bins == NULL) new_tab->bins = NULL; @@ -1246,7 +1239,6 @@ st_copy(st_table *old_tab) new_tab->bins = (st_index_t *) malloc(bins_size(old_tab)); #ifndef RUBY if (new_tab->bins == NULL) { - free(new_tab); return NULL; } #endif @@ -1255,7 +1247,6 @@ st_copy(st_table *old_tab) * sizeof(st_table_entry)); #ifndef RUBY if (new_tab->entries == NULL) { - st_free_table(new_tab); return NULL; } #endif @@ -1263,6 +1254,27 @@ st_copy(st_table *old_tab) get_allocated_entries(old_tab)); if (old_tab->bins != NULL) MEMCPY(new_tab->bins, old_tab->bins, char, bins_size(old_tab)); + + return new_tab; +} + +/* Create and return a copy of table OLD_TAB. */ +st_table * +st_copy(st_table *old_tab) +{ + st_table *new_tab; + + new_tab = (st_table *) malloc(sizeof(st_table)); +#ifndef RUBY + if (new_tab == NULL) + return NULL; +#endif + + if (st_replace(new_tab, old_tab) == NULL) { + st_free_table(new_tab); + return NULL; + } + return new_tab; } |