develooper Front page | perl.perl5.porters | Postings from September 2010

[perl #77812] Globs in tied scalars can be reified if STORE dies

Father Chrysostomos
September 12, 2010 12:41
[perl #77812] Globs in tied scalars can be reified if STORE dies
Message ID:
# New Ticket Created by  Father Chrysostomos 
# Please include the string:  [perl #77812]
# in the subject line of all future correspondence about this issue. 
# <URL: >

Another regression in 5.13, this one caused by 0fe688f528. Some of these patch authors are clueless!

#!perl -l
sub TIESCALAR{bless[]}
sub STORE{ die "No!"}
sub FETCH{ *foo }
tie $a, "";
() = $a; # do a fetch
eval { *$a = sub{} };
# From this point on $a holds a glob without the FAKE flag.
eval { $a = undef };
untie $a;
$a = "bar";
print $a;

In 5.12 and earlier this prints "bar".

In 5.13.4 the penultimate line dies with "No!", because it’s still tied.

This is because of the order of these statements in pp_sassign is wrong:

    /* Allow glob assignments like *$x = ..., which, when the glob has a
       SVf_FAKE flag, cannot be distinguished from $x = ... without looking
       at the op tree. */
    if( SvTYPE(right) == SVt_PVGV && cBINOP->op_last->op_type == OP_RV2GV
     && (wasfake = SvFLAGS(right) & SVf_FAKE) )
        SvFLAGS(right) &= ~SVf_FAKE;
    SvSetMagicSV(right, left);
    if(wasfake) SvFLAGS(right) |= SVf_FAKE;

Turning the fake flag back on should happen before magic is called.

If bug #77810 is fixed, we can simply revert the patch that caused this, and put the appropriate logic in one place.

This same bug also affects a patch I wrote for #77508 (list assignment to dereferenced fake glob).

Here is the example script again, with more explanatory notes:

#!perl -l
sub TIESCALAR{bless[]}
sub STORE{ die "No you don::t!"}
sub FETCH{ *foo }
tie $a, "";
() = $a; # do a fetch

# Now the $a scalar holds a copy of *foo; i.e., a PVGV with the FAKE flag
# on, indicating that this is not really a glob, but just a scalar holding
# a copy of one.

eval { *$a = sub{} };
# pp_sassign (the function implementing the = operator) turns off the FAKE
# flag temporarily before assignment, because of the * (rv2gv) on the
# lefthand side.
# Set-magic (called right after the assignment) dies (in the STORE routine
# above) before the FAKE flag is turned back on. As a result, it stays off,
# so what we now have looks exactly like a glob in the symbol table.

eval { $a = undef };
# This is a workaround for a bug still present in blead. untie() won’t
# untie a scalar if it sees a glob, whether FAKE or not.
# We try to assign undef to $a, so that the $a scalar will itself hold
# undef, even if STORE dies. But, since the FAKE flag is gone, we are
# asigning to a ‘real’ glob. Assigning undef to a glob does nothing (except
# warn).

# Untie the variable. Since there is a glob, it tries to untie the IO slot,
# which doesn’t exist, so nothing happens.
untie $a;

# We are supposedly assigning "bar" to an innocent $a variable, but in
# truth we are making the glob in $a an alias to *bar. 
$a = "bar";
# That assignment triggers set-magic, which calls STORE, which dies.

# We never get here.

print $a;

At this point (suppose all the above was in an eval), even if the FETCH routine starts returning undef, it will always be the glob that is returned to the calling code.

This would make it rather difficult to implement read-only aliases using ties. Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About