Skip to content
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

tie then untie on a hash that is midway through iteration mangles the hash iterator state #19077

Closed
nwc10 opened this issue Aug 23, 2021 · 1 comment
Assignees

Comments

@nwc10
Copy link
Contributor

nwc10 commented Aug 23, 2021

Description

tie then untie on a hash that is midway through iteration mangles the hash iterator state. After the untie where hash iteration resumes is not consistent, and depends on whether tie chanced to happen when the iterator was midway down a hash chain.

Steps to Reproduce

$ cat collide.pl
use strict;
use warnings;
use Hash::Util qw(used_buckets);
require Tie::Hash;

sub testit {
    my @keys = @_;
    my %h;
    ++$h{$_} for @keys;

    my $k1 = each %h;

    tie %h, 'Tie::StdHash';
    untie %h;

    my @have;
    while (1) {
        my $k = each %h;
        last
            unless defined $k;
        push @have, $k;
    }

    print "For @keys, have @have\n";
    return;
}

LOOP:
for my $x ( "A" .. "Z" ) {
    for my $y ( chr( ord($x) + 1 ) .. "Z" ) {
        my %h;
        ++$h{$x};
        ++$h{$y};
        if (used_buckets %h == 1) {
            testit($x, $y);
            last LOOP;
        }
    }
}

testit('A', 'B');

__END__
$ perl collide.pl
For A F, have
For A B, have B

(Note that for this run A and F were the first pair of keys that were found to collide into the same hash chain, and (fortunately) A and B did not collide)

Expected behaviour

Expected behaviour would be "something consistent" - whether the keys collide or not the state is the same.

The bug is in this code in pp_tie:

        case SVt_PVHV:
        {
            HE *entry;
            methname = "TIEHASH";
            if (HvLAZYDEL(varsv) && (entry = HvEITER_get((HV *)varsv))) {
                HvLAZYDEL_off(varsv);
                hv_free_ent((HV *)varsv, entry);
            }
            HvEITER_set(MUTABLE_HV(varsv), 0);
            break;
        }

which (re)sets EITER but not RITER when the hash is tied, meaning that RITER is unchanged.

Fix coming once I have an issue ID.

@nwc10 nwc10 self-assigned this Aug 23, 2021
nwc10 added a commit to nwc10/perl5 that referenced this issue Aug 23, 2021
Previously it would mangle it, resetting EITER but not RITER, meaning that
after untie continuing iteration would be inconsistent - normally it would
carry on exactly where it left off, but if iteration had been in the middle
of a chain of HEs, it would skip the rest of the chain.

Fixes GH Perl#19077
nwc10 added a commit to nwc10/perl5 that referenced this issue Aug 23, 2021
Previously it would mangle it, resetting EITER but not RITER, meaning that
after untie continuing iteration would be inconsistent - normally it would
carry on exactly where it left off, but if iteration had been in the middle
of a chain of HEs, it would skip the rest of the chain.

Fixes GH Perl#19077
nwc10 added a commit to nwc10/perl5 that referenced this issue Aug 24, 2021
Previously it would mangle it, resetting EITER but not RITER, meaning that
after untie continuing iteration would be inconsistent - normally it would
carry on exactly where it left off, but if iteration had been in the middle
of a chain of HEs, it would skip the rest of the chain.

Fixes GH Perl#19077
tonycoz pushed a commit that referenced this issue Aug 25, 2021
Previously it would mangle it, resetting EITER but not RITER, meaning that
after untie continuing iteration would be inconsistent - normally it would
carry on exactly where it left off, but if iteration had been in the middle
of a chain of HEs, it would skip the rest of the chain.

Fixes GH #19077
@nwc10
Copy link
Contributor Author

nwc10 commented Aug 28, 2021

Fixed by 364906c

@nwc10 nwc10 closed this as completed Aug 28, 2021
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

No branches or pull requests

1 participant