diff options
author | Peter Zhu <peter@peterzhu.ca> | 2022-10-05 15:19:43 -0400 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2022-10-06 09:01:12 -0400 |
commit | 76bae60d9b967415c5930c2c5906e14c8362a6dd (patch) | |
tree | 0cb9fba5afd5eac99ed5e7d1cd2f8799b3adb625 /st.c | |
parent | e696ec67ac7bd14ff8436f9ee7724c17c5bf6689 (diff) | |
download | ruby-76bae60d9b967415c5930c2c5906e14c8362a6dd.tar.gz |
[Bug #19038] Fix corruption of generic_iv_tbl when compacting
When the generic_iv_tbl is resized up, rebuild_table performs
allocations that can trigger GC. If autocompaction is enabled, then
moved objects are removed from and inserted into the generic_iv_tbl.
This may cause another call to rebuild_table to resize the
generic_iv_tbl. When returning back to the original rebuild_table, some
of the data may be stale, causing the generic_iv_tbl to be corrupted.
This commit changes rebuild_table to only read data from the st_table
after the allocations have completed.
Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>
Diffstat (limited to 'st.c')
-rw-r--r-- | st.c | 15 |
1 files changed, 10 insertions, 5 deletions
@@ -703,16 +703,14 @@ count_collision(const struct st_hash_type *type) static void rebuild_table(st_table *tab) { - st_index_t i, ni, bound; + st_index_t i, ni; unsigned int size_ind; st_table *new_tab; - st_table_entry *entries, *new_entries; + st_table_entry *new_entries; st_table_entry *curr_entry_ptr; st_index_t *bins; st_index_t bin_ind; - bound = tab->entries_bound; - entries = tab->entries; if ((2 * tab->num_entries <= get_allocated_entries(tab) && REBUILD_THRESHOLD * tab->num_entries > get_allocated_entries(tab)) || tab->num_entries < (1 << MINIMAL_POWER2)) { @@ -721,16 +719,23 @@ rebuild_table(st_table *tab) if (tab->bins != NULL) initialize_bins(tab); new_tab = tab; - new_entries = entries; + new_entries = tab->entries; } else { + /* This allocation could trigger GC and compaction. If tab is the + * gen_iv_tbl, then tab could have changed in size due to objects being + * freed and/or moved. Do not store attributes of tab before this line. */ new_tab = st_init_table_with_size(tab->type, 2 * tab->num_entries - 1); new_entries = new_tab->entries; } + ni = 0; bins = new_tab->bins; size_ind = get_size_ind(new_tab); + st_index_t bound = tab->entries_bound; + st_table_entry *entries = tab->entries; + for (i = tab->entries_start; i < bound; i++) { curr_entry_ptr = &entries[i]; PREFETCH(entries + i + 1, 0); |