Bug 2055608

Summary: Perl 5.32.1 Incorrectly Processes Hash Key Existence in Two-Dimensional Hashes
Product: [Fedora] Fedora Reporter: BZ <bgz>
Component: perlAssignee: Jitka Plesnikova <jplesnik>
Status: CLOSED NOTABUG QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: medium Docs Contact:
Priority: unspecified    
Version: 34CC: caillon+fedoraproject, iarnell, jplesnik, kasal, mmaslano, mspacek, perl-devel, ppisar, psabata, rhughes, sandmann, spotrh
Target Milestone: ---   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2022-02-17 11:42:53 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description BZ 2022-02-17 11:08:25 UTC
Description of problem:

This appears to be a basic Perl runtime error. Checking for the existence of an element in a two-dimensional hash has the side-effect of bringing into existence a hash entry with the first index value. It's easier to see in code:

#!/usr/bin/perl

%X = ();
@I = ("1","2","3");
$N = 0;
foreach $D (@I)
{
    $N++ if (exists($X{$D}{"A"}));
    printf "K: %u\n",scalar(keys %X);
}
printf "N: %u\n",$N;

Running the code inexplicably generates this output:

K: 1
K: 2
K: 3
N: 0

Version-Release number of selected component (if applicable):

5.32.1 with 51 patches applied by Fedora (this is the current package version available in Fedora 34)

How reproducible:

100%

Steps to Reproduce:
1. Run the above code
2.
3.

Actual results:

K: 1
K: 2
K: 3
N: 0

Expected results:

K: 0
K: 0
K: 0
N: 0

Additional info:

The problem only seems to arise with multi-dimensional hashes. Code such as:

#!/usr/bin/perl

%X = ();
@I = ("1","2","3");
$N = 0;
foreach $D (@I)
{
    $N++ if (exists($X{$D}));
    printf "K: %u\n",scalar(keys %X);
}
printf "N: %u\n",$N;

behaves as expected, returning:

K: 0
K: 0
K: 0
N: 0

I find it difficult to believe that this bug, if it is a bug rather than a not-very-well-documented "feature," has not been noticed before, but perhaps nothing critical depended upon it? But then again, something critical might, in which case this package version dates back to 23 June 2021...

Comment 1 Jitka Plesnikova 2022-02-17 11:42:53 UTC
This behavior is describe in perldoc for 'exists':


Note that the EXPR can be arbitrarily complicated as long as the 
final operation is a hash or array key lookup or subroutine
name:

    if (exists $ref->{A}->{B}->{$key})  { }
    if (exists $hash{A}{B}{$key})       { }

    if (exists $ref->{A}->{B}->[$ix])   { }
    if (exists $hash{A}{B}[$ix])        { }

    if (exists &{$ref->{A}{B}{$key}})   { }

Although the most deeply nested array or hash element will not
spring into existence just because its existence was tested, any 
intervening ones will. Thus "$ref->{"A"}" and
"$ref->{"A"}->{"B"}" will spring into existence due to the
existence test for the $key element above. This happens anywhere
the arrow operator is used, including even here:

    undef $ref;
    if (exists $ref->{"Some key"})    { }
    print $ref;  # prints HASH(0x80d3d5c)

Comment 2 Petr Pisar 2022-02-17 11:46:05 UTC
This feature is called "autovivification". See perlref POD.

Comment 3 BZ 2022-02-17 22:39:16 UTC
Thanks for that. Even though semantically it seems like it SHOULD be a bug, for better or worse at least it's not something unexpected. I just didn't find any reference to that behavior in the documents I checked. Thanks!