On Fri, Apr 18, 2025 at 11:12 PM Shim Manning <[email protected]> wrote:
>
> Seems that using .EXTRA_PREREQS can cause a segfault under the right
> conditions.
...
> snap_deps
> hash_map_arg (loop happens here)
> snap_file
> expand_extra_prereqs
> enter_file
> hash_insert_at
> hash_rehash (loop no longer valid)
>
> causes a fairly consistent crash. I'm working on a minimal reproduction if
> it's necessary, but allowing the table to be rehashed while iterating seems
> like a problem.
Thank you for your report.
A test case to reproduce would be good. We'll need to add a test along
with a fix.
Given that you have a setup that crashes consistently, let me ask you
to try out this fix and tell us if this helps.
diff --git a/src/hash.c b/src/hash.c
index 41e16895..23d6cd4d 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -235,14 +235,16 @@ hash_map (struct hash_table *ht, hash_map_func_t map)
void
hash_map_arg (struct hash_table *ht, hash_map_arg_func_t map, void *arg)
{
- void **slot;
- void **end = &ht->ht_vec[ht->ht_size];
-
- for (slot = ht->ht_vec; slot < end; slot++)
- {
- if (!HASH_VACANT (*slot))
- (*map) (*slot, arg);
- }
+ /* Need to call 'map' for each item in 'ht'.
+ Cannot iterate through 'ht', because 'map' can attempt to insert to 'ht'
+ and cause 'ht' to be rehashed. Copy all items from 'ht' to a temporary
+ array 't' and then iterate through the temporary array. */
+ const struct file **t;
+ t = alloca (sizeof (const struct file *) * (ht->ht_fill + 1));
+ hash_dump (ht, (void **) t, 0);
+ /* t is null terminated. */
+ for (; *t; ++t)
+ (*map) (*t, arg);
}
/* Double the size of the hash table in the event of overflow... */
regards, Dmitry