aboutsummaryrefslogtreecommitdiffstats
path: root/st.c
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-06-29 09:21:11 -0400
committerPeter Zhu <peter@peterzhu.ca>2023-06-29 11:16:50 -0400
commitf0d08d11dcd404f3146c0d71d6ff743bbc6e7193 (patch)
tree82e6bb0fff741e124915c7a2f6aabe54a484ee71 /st.c
parentdf2b3a29987e9353596af76ed77f35d7366d654e (diff)
downloadruby-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.c34
1 files changed, 23 insertions, 11 deletions
diff --git a/st.c b/st.c
index 7d44171e97..119a6964b4 100644
--- a/st.c
+++ b/st.c
@@ -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;
}