Skip to content

Detect rehash in array-backed hashes #13919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 18, 2025
Merged

Detect rehash in array-backed hashes #13919

merged 3 commits into from
Jul 18, 2025

Conversation

sferik
Copy link
Contributor

@sferik sferik commented Jul 16, 2025

Added safeguards for array-backed hashes by storing the table pointer and bound in hash_foreach_arg and checking them during iteration to raise an exception if a rehash occurs.

Fixed TODO added in 2018 by @ko1.

This comment has been minimized.

@sferik sferik force-pushed the detect_rehash branch 3 times, most recently from b1a72a3 to 243d5ee Compare July 16, 2025 22:40
hash.c Outdated
int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
/* TODO: rehash check? rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); */
if (!RHASH_AR_TABLE_P(arg->hash) || RHASH_AR_TABLE(arg->hash) != tbl) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget what happens on rehash, but RHASH_AR_TABLE(arg->hash) != tbl is correct checking?

hash.c Outdated
int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
/* TODO: rehash check? rb_raise(rb_eRuntimeError, "rehash occurred during iteration"); */
if (!RHASH_AR_TABLE_P(arg->hash) || RHASH_AR_TABLE(arg->hash) != tbl) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is copying from similar code using RHASH_ST_TABLE, but that check might actually never trigger. RHASH_ST_TABLE() just adds an offset to the argument, so for the same hash, it's always going to be the same. arg->hash is never going to be changed by the callback, so that check will always be false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this condition to check if the bound increases. That should work, right?

@XrXr
Copy link
Member

XrXr commented Jul 17, 2025

@ko1 is this check and the on for ST table meant to be a debug assert? Seems like it's not possible to accurately check for rehash here efficiently. Maybe we should just remove the code and TODO, or turn them into RUBY_ASSERT.

st_table *tbl = RHASH_ST_TABLE(arg->hash);
int status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);

if (RHASH_ST_TABLE(arg->hash) != tbl) {
    rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
}

With recent changes it never raises:

static inline st_table *
RHASH_ST_TABLE(VALUE h)
{
    return (st_table *)((uintptr_t)h + sizeof(struct RHash));
}

Also, Hash#rehash disallows running while iterating, so it's unclear how these checks could fail in practice:

if (hash_iterating_p(hash)) {
    rb_raise(rb_eRuntimeError, "rehash during iteration");
}

@ko1
Copy link
Contributor

ko1 commented Jul 17, 2025

@ko1 is this check and the on for ST table meant to be a debug assert? Seems like it's not possible to accurately check for rehash here efficiently. Maybe we should just remove the code and TODO, or turn them into RUBY_ASSERT.

+1 for removing the code and TODO.

@sferik
Copy link
Contributor Author

sferik commented Jul 17, 2025

Okay, I've removed the code I added and the TODO. Would you like me to squash this into a single commit or will you squash when you merge?

@XrXr XrXr merged commit bd27460 into ruby:master Jul 18, 2025
84 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy