This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #85670] Copy magic to ary elems properly
authorFather Chrysostomos <sprout@cpan.org>
Sat, 7 Jan 2012 07:36:38 +0000 (23:36 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 7 Jan 2012 07:36:38 +0000 (23:36 -0800)
commit70ce9249c4e5e892ce6ec830baedb9e3aed67ded
tree302a9e575fbd47a6671efba850f96838a296248e
parent9f71cfe6ef2a57e26394d4caf1bf2894802f4777
[perl #85670] Copy magic to ary elems properly

On Tue Mar 08 07:26:35 2011, thospel wrote:
> #!/usr/bin/perl -l
> use Data::Dumper;
> use Scalar::Util qw(weaken);
> our @ISA;
>
> for (1..2) {
>     @ISA = qw(Foo);
>     weaken($a = \@ISA);
>     weaken($a = \$ISA[0]);
>     print STDERR Dumper(\@ISA);
> }
>
> This prints:
> $VAR1 = [
>           'Foo'
>         ];
> $VAR1 = [
>           'Foo',
>           \$VAR1->[0]
>         ];
>
> So the first time it's the expected @ISA, but the second time round it
>    automagically added a reference to to the first ISA element
>
> (bug also exists in blead)

Shorter:

#!/usr/bin/perl -l

use Scalar::Util qw(weaken);

weaken($a = \@ISA);
@ISA = qw(Foo);
use Devel::Peek; Dump \@ISA;
weaken($a = \$ISA[0]);
print scalar @ISA;   # prints 2

The dump shows the problem.  backref magic is being copied to the ele-
ment.  Put the magic in a different order, and everything is fine:

#!/usr/bin/perl -l

use Scalar::Util qw(weaken);

weaken($a = $b = []);
*ISA = $a;
@ISA = qw(Foo);
use Devel::Peek; Dump \@ISA;
weaken($a = \$ISA[0]);
print scalar @ISA;   # prints 2

This code in av_store is so wrong:

    if (SvSMAGICAL(av)) {
const MAGIC* const mg = SvMAGIC(av);
if (val != &PL_sv_undef) {
    sv_magic(val, MUTABLE_SV(av), toLOWER(mg->mg_type), 0, key);
}
if (PL_delaymagic && mg->mg_type == PERL_MAGIC_isa)
    PL_delaymagic |= DM_ARRAY_ISA;
else
   mg_set(MUTABLE_SV(av));
    }

It doesn’t follow the magic chain at all.  So anything magic could get
attached to the @ISA array, and that will be copied to the element
instead of isa magic.

Notice that MUTABLE_SV(av) is the second argument to sv_magic, so
mg->mg_obj for the element always points back to the array.

Since backref magic’s mg->mg_obj points to the backrefs array, @ISA
ends up being used as this element’s backrefs array.

What if arylen_p gets copied instead?  Let’s see:

$#ISA = -1;
@ISA = qw(Foo);
$ISA[0] = "Bar";

main->ber;

sub Bar::ber { warn "shave" }
__END__
Can't locate object method "ber" via package "main" at - line 7.

I’ve fixed this by making av_store walk the magic chain, copying any
magic for which toLOWER(mg->mg_type) != mg->mg_type.
av.c
t/op/array.t