develooper Front page | perl.perl5.porters | Postings from January 2003

Re: [perl #9394] Re: [ID 20020525.002] coredump/bad free warning in blead with SIGWARN

Nicholas Clark
January 17, 2003 15:52
Re: [perl #9394] Re: [ID 20020525.002] coredump/bad free warning in blead with SIGWARN
Message ID:
On Fri, Jan 17, 2003 at 09:46:40PM +0000, Nicholas Clark wrote:

> I have Jarkko's test case down to:
> #!./perl
> my @warnings;
>     push @warnings, \'FOO';
> }
> my $instruction = shift @warnings;
> $instruction = $$instruction;

$mild_expletive that:

perl -le 'my $a; BEGIN {$a = \"Foo"}; $a = $$a;
Segmentation fault (core dumped)

take out the my, no SEGV. However, if you try to print $a, you see that
something is going wrong:

$ perl -le '$a; BEGIN {$a = \"Foo"}; $a = $$a; print $a'

Dumping (as before)

$ ./perl -Ilib -MDevel::Peek -le '$a; BEGIN {$a = \"Foo"}; Dump $a; $a = $$a; Dump $a'
SV = RV(0x817ec28) at 0x817383c
  REFCNT = 1
  RV = 0x817386c
  SV = PV(0x8164580) at 0x817386c
    REFCNT = 1
    PV = 0x8163f50 "Foo"\0
    CUR = 3
    LEN = 4
SV = UNKNOWN(0xff) (0x817ec28) at 0x817383c
  REFCNT = 1
  FLAGS = ()

> What is so special about the BEGIN block that it is trashing the stack,
> and causing a later SEGV? Is there some dangling reference to an SV
> constant held in an OP that gets freed up when the BEGIN block completes?
> (this is still a bug in current blead)


Perl_sv_setsv_flags (dstr=0x81707d8, sstr=0x8173c6c, flags=2) at sv.c:3572
3572        if (sstr == dstr)
(gdb) call Perl_sv_dump(dstr)
SV = RV(0x8175410) at 0x81707d8
  REFCNT = 1
  RV = 0x8173c6c
(gdb) call Perl_sv_dump(sstr)
SV = PV(0x8164550) at 0x8173c6c
  REFCNT = 1
  PV = 0x8163ef0 "Foo"\0
  CUR = 3
  LEN = 4
(gdb) step
(gdb) next
3575        if (!sstr)
(gdb) call Perl_sv_dump(sstr)
SV = UNKNOWN(0xff) (0x816415c) at 0x8173c6c
  REFCNT = 0
  FLAGS = ()

In the assignment $a = $$a, $a (the destination) holds the last reference
to $$a (the source). So at that SV_CHECK_THINKFIRST_COW_DROP(dstr); clears
the destination, which causes the refcount of the source to drop to zero,
and it gets freed. Whoops.

All this happens due to a rather unusual combination of circumstances. There
is no IMMEDIATE_UNREF flag, so one wouldn't expect an immediate unref.
However, the logic in Perl_sv_unref_flags goes like this:

    if (SvREFCNT(rv) != 1 || SvREADONLY(rv) || (flags & SV_IMMEDIATE_UNREF))
    else /* XXX Hack, but hard to make $a=$a->[1] work otherwise */
	sv_2mortal(rv);		/* Schedule for freeing later */

so as the rv is READONLY (it's the readonly string "Foo") it gets zapped.

The reason we don't normally see this is because usually there's an OP
holding onto another reference for the constant. So the destination scalar
isn't the last referent.

Now that I know the cause of the problem, here's another without the BEGIN
block, albeit 5.8.0 specific.

$ ./perl -Ilib -MDevel::Peek -le '%a = perl=>"rules"; my $a = \keys %a; Dump $a; $a = $$a; Dump $a'
SV = RV(0x817ec28) at 0x81738b4
  REFCNT = 1
  RV = 0x81641ec
  SV = PVIV(0x8164820) at 0x81641ec
    REFCNT = 1
    IV = -1271882440
    PV = 0x81630f8 "perl"
    CUR = 4
    LEN = 0
SV = UNKNOWN(0xff) (0x817ec28) at 0x81738b4
  REFCNT = 1
Bus error (core dumped)

I think that the READONLY test should go. Any reason why it must stay?
Nicholas Clark Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About