in reply to Use of freed value in iteration

Hello, I have a question related to this "Use of freed value in iteration". Is it possible for the "freed" value to to be replaced by some other value in memory unintentionally, via some unrelated variables?

For example, the following code will produce the "Use of freed value in iteration" error:

my $hr1 = {}; my $hr2 = {}; my $hr3 = {"$hr1" => $hr1, "$hr2" => $hr2}; my @values = sort values %$hr3; print "@values\n"; foreach my $v (sort values %$hr3) { print "$v\n"; delete $hr3->{$values[1]}; # some_function() }
but say some_function does a bunch of other stuff. Is it possible for the value of the second item in the foreach loop to spring back into existence? For example, if some_function reuses the memory location that $hr3->{$values[1]} once used?

Replies are listed 'Best First'.
Re^2: Use of freed value in iteration
by Danny (Chaplain) on Feb 11, 2024 at 23:06 UTC
    After doing a simple test this definitely is the case. If I simply push a string onto an array after deleting the hash key $v ends up getting the string. On my machine the following code prints:
    HASH(0xa00003da0) HASH(0xa00003f38) HASH(0xa00003da0) hello
    my $hr1 = {}; my $hr2 = {}; my $hr3 = {"$hr1" => $hr1, "$hr2" => $hr2}; my @values = sort values %$hr3; print "@values\n"; my (@arr); foreach my $v (sort values %$hr3) { print "$v\n"; delete $hr3->{$values[1]}; push @arr, "hello"; }
    This seems very dangerous.

      The code is modifying the hash while iterating over its values and using those values as the keys for deletion. You perhaps already tried this, but if you change the loop to iterate over @values or the keys of $hr3 then the issue disappears. Same if you stringify the loop iterator $v.

      Others can better comment on the details, but I assume perl is re-using memory and your code is reaching into the old locations.

        Yes, that is the point. The warning "Use of freed value in iteration" is great for catching bugs, but it can easily fail to catch one if the memory location of the deleted item is replaced with something else. If that something else happens to be the same type as the original item, then it could be a really insidious bug.
        "but if you change the loop to iterate over @values or the keys of $hr3 then the issue disappears"

        Yeah, iterating over @values would be really bad if you wanted to ensure you are only dealing with things defined in $hr3. Iterating over the keys of %$hr3 is definitely the safer way to go. That way when $hr3->{$key} is accessed you'll get a warning if it doesn't exist.

      For completeness, this value replacement also occurs when iterating over values of an array, and the replacement value can be from a new hash key or a new array element. The following code
      my @arr = ("item 1","item 2"); my %h; foreach my $v (values @arr) { print "$v\n"; delete $arr[1]; $h{x} = "sneaky hash value" } @arr = ("item 1","item 2"); my @arr2; foreach my $v (values @arr) { print "$v\n"; delete $arr[1]; push @arr2, "sneaky array value"; }
      produces
      item 1 sneaky hash value item 1 sneaky array value