Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
2.07k stars 574 forks source link

coredump/bad free warning in blead with SIGWARN #5484

Closed p5pRT closed 21 years ago

p5pRT commented 22 years ago

Migrated from rt.perl.org#9394 (status was 'resolved')

Searchable as RT9394$

p5pRT commented 22 years ago

From @nwc10

Created by @nwc10

I've managed to do something evil to bleadperl (this is patch 16767) Segmentation fault (core dumped)

(gdb) where #0 0x80a177a in S_new_xrv () #1 0x80a1fe5 in Perl_sv_upgrade () #2 0x80a4dc2 in Perl_sv_setsv_flags () #3 0x809abfe in Perl_pp_sassign () #4 0x809a8d0 in Perl_runops_standard () #5 0x805eace in S_call_body () #6 0x805e8b2 in Perl_call_sv () #7 0x8061322 in S_call_list_body () #8 0x8061030 in Perl_call_list () #9 0x805c4a7 in perl_destruct () #10 0x805b920 in main () #11 0x805b7fd in _start ()

It's a bit late\, and I've not done too well at pruning the test case because I need to go to bed. The simpler version is this​:

#!./perl -w

my @​warnings; sub BEGIN {   $SIG{__WARN__} = sub {   push @​warnings\, $_[0];   print STDERR $_[0];   }; }

use strict;

BEGIN {   push @​warnings\, \'Storable​::FOO'; } $Storable​::FOO = 1;

print "'$_'\n"\, @​warnings;

my $unexpected = 0; while (@​warnings and my $instruction = shift @​warnings) {   $instruction = $$instruction;   warn $instruction;   my ($expect\, $got);   if ($instruction =~ /^!(.*)/) {   # Don't expect to see a warning for this variable.   # So should either be at the end\, or next item is another instruction.   } else {   # Expect a used only once warning.   $expect = "/^Name \"$instruction\" used only once/";   }

  if (@​warnings and ref $warnings[0]) {   # Something not an instruction follows us.   $got = shift @​warnings;   # Clear any extra unexpected warnings.   while (@​warnings and !ref $warnings[0]) {   shift @​warnings;   $unexpected++;   }   } } __END__

On FreeBSD that gives this slew of warnings\, most of which are expected​:

Name "Storable​::FOO" used only once​: possible typo at /home/nick/test/CorePerl line 16. Use of uninitialized value in concatenation (.) or string at /home/nick/test/CorePerl line 18. '' SCALAR(0x8123d44)Name "Storable​::FOO" used only once​: possible typo at /home/nick/test/CorePerl line 16. Use of uninitialized value in concatenation (.) or string at /home/nick/test/CorePerl line 18. Use of uninitialized value in warn at /home/nick/test/CorePerl line 23. Warning​: something's wrong at /home/nick/test/CorePerl line 23. Use of uninitialized value in pattern match (m//) at /home/nick/test/CorePerl line 25. Use of uninitialized value in concatenation (.) or string at /home/nick/test/CorePerl line 30. perl16767-32 in free()​: warning​: modified (chunk-) pointer Can't use string ("Name "Storable​::FOO" used only o") as a SCALAR ref while "strict refs" in use at /home/nick/test/CorePerl line 22.

BUT NOT THAT ONE​: perl16767-32 in free()​: warning​: modified (chunk-) pointer

Something is scribbling on RAM. The original program gave this​:

1..2 ok 1 - No warnings before we start Use of uninitialized value in pattern match (m//) at t/variables.t line 73. Use of uninitialized value in concatenation (.) or string at t/variables.t line 78. Use of uninitialized value in concatenation (.) or string at t/variables.t line 92. not ok 2 - Should be a warning for # Failed test (t/variables.t at line 90) # 'SCALAR(0x8119fcc)' # doesn't match '/^Name "" used only once/' Can't use string ("Use of uninitialized value in co") as a SCALAR ref while "strict refs" in use at t/variables.t line 71. Segmentation fault (core dumped)

I can't see why the logic should end up with me attempting to dereferencing a non-reference. Or why the string has been truncated\, although that looks like it's a side effect of RAM scribbling.

The original program is somewhat longer. I include it\, because it does SEGV for me\, whereas my cutdown version does not.

#!./perl -w

# # Copyright 2002\, Larry Wall. # # You may redistribute only under the same terms as Perl 5\, as specified # in the README file that comes with the distribution. #

# I ought to keep this test easily backwards compatible to 5.004\, so no # qr//;

# This test checks downgrade behaviour on pre-5.8 perls when new 5.8 features # are encountered.

# This might be rather sick way of doing things\, but it should work without # spawning a new perl.

# Array accumulates things we're trying (as refs to scalar)\, interspersed with # warnings seen.

my @​warnings; sub BEGIN {   if ($ENV{PERL_CORE}){   chdir('t') if -d 't';   @​INC = ('.'\, '../lib');   } else {   unshift @​INC\, 't';   }   require Config; import Config;   if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) {   print "1..0 # Skip​: Storable was not built\n";   exit 0;   }   $SIG{__WARN__} = sub {   push @​warnings\, $_[0];   print STDERR $_[0]   unless $_[0] =~ /^Name "Storable​::[a-zA-Z_]+" used only once/;   }; }

use Test​::More tests => 2; use Storable; use strict;

BEGIN {   push @​warnings\, \'Storable​::FOO'; } $Storable​::FOO = 1;

# The idea is that we don't have DEBUGME enabled on shipping code. BEGIN {   push @​warnings\, \'Storable​::DEBUGME'; # Grr' } $Storable​::DEBUGME = 1;

# Coredump (no change actually) with our without this one​: delete $SIG{__WARN__};

# Now eat our warnings. my $early = 0; while (!ref $warnings[0]) {   shift @​warnings;   $early++; } is ($early\, 0\, "No warnings before we start");

my $unexpected = 0; while (@​warnings and my $instruction = shift @​warnings) {   $instruction = $$instruction;   my ($expect\, $got);   if ($instruction =~ /^!(.*)/) {   # Don't expect to see a warning for this variable.   # So should either be at the end\, or next item is another instruction.   } else {   # Expect a used only once warning.   $expect = "/^Name \"$instruction\" used only once/";   }

  if (@​warnings and ref $warnings[0]) {   # Something not an instruction follows us.   $got = shift @​warnings;   # Clear any extra unexpected warnings.   while (@​warnings and !ref $warnings[0]) {   shift @​warnings;   $unexpected++;   }   }   if ($expect) {   # expecting a warning.   like ($got\, $expect\, "Should be a warning for $instruction");   } else {   is ($got\, $expect\, "Should be no warning for $1");   } }

is ($unexpected\, 0\, "No unexpected warnings"); __END__

Hopefully someone in a more appropriate timezone who isn't going out tomorrow can cut this down to a terse coredump\, or better still patch it.

Bug seems to be present in 5.6.1\, 5.6.0\, 5.005_03 and 5.004_05 too\, although 5.6.0 isn't showing the warning from FreeBSD malloc about modified (chunk-) pointer

Nicholas Clark

Perl Info ``` Flags: category=core severity=low This perlbug was built using Perl v5.7.3 - Sat May 25 15:42:18 BST 2002 It is being executed now by Perl v5.7.3 - Fri May 24 21:02:00 BST 2002. Site configuration information for perl v5.7.3: Configured by nick at Fri May 24 21:02:00 BST 2002. Summary of my perl5 (revision 5.0 version 7 subversion 3 patch 16767) configuration: Platform: osname=freebsd, osvers=4.5-stable, archname=i386-freebsd uname='freebsd thinking-cap.moo 4.5-stable freebsd 4.5-stable #1: wed feb 6 16:15:14 gmt 2002 nick@thinking-cap.moo:stuffusrsrcsyscompilethinkingcap i386 ' config_args='-de -Dcc=ccache gcc -Dld=gcc -Dusedevel -Ubincompat5005 -Doptimize=-Os -Uinstallusrbinperl -Dcf_email=nick@ccl4.org -Dperladmin=nick@ccl4.org -Dinc_version_list= -Dinc_version_list_init=0 -Dinstallman1dir=none -Dinstallman3dir=none' hint=recommended, useposix=true, d_sigaction=define usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='ccache gcc', ccflags ='-DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -I/usr/local/include', optimize='-Os', cppflags='-DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing -I/usr/local/include' ccversion='', gccversion='2.95.3 20010315 (release) [FreeBSD]', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='gcc', ldflags ='-Wl,-E -L/usr/local/lib' libpth=/usr/lib /usr/local/lib libs=-lm -lc -lcrypt -lutil perllibs=-lm -lc -lcrypt -lutil libc=, so=so, useshrplib=false, libperl=libperl.a Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' ' cccdlflags='-DPIC -fpic', lddlflags='-shared -L/usr/local/lib' Locally applied patches: DEVEL16763 @INC for perl v5.7.3: /usr/local/lib/perl5/5.7.3/i386-freebsd /usr/local/lib/perl5/5.7.3 /usr/local/lib/perl5/site_perl/5.7.3/i386-freebsd /usr/local/lib/perl5/site_perl/5.7.3 /usr/local/lib/perl5/site_perl . Environment for perl v5.7.3: HOME=/home/nick LANG (unset) LANGUAGE (unset) LC_CTYPE=en_GB.ISO_8859-1 LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/nick/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/home/nick/bin:/sbin:/usr/sbin:/usr/local/sbin PERL_BADLANG (unset) SHELL=/usr/local/bin/bash Reply-To: ```
p5pRT commented 22 years ago

From @jhi

Using your simpler version\, 16792\, and Third Degree I get this before the core dump​:

---------------------------------------------------------------- fih -- 7 -- util.c​: 151​: freeing heap at byte 32 of 1008-byte block   free libc.so   Perl_safesysfree libperl.so\, util.c\, line 151   Perl_sv_setsv_flags libperl.so\, sv.c\, line 3911   Perl_pp_sassign libperl.so\, pp_hot.c\, line 108   Perl_runops_debug libperl.so\, dump.c\, line 1398   S_run_body libperl.so\, perl.c\, line 1673   perl_run libperl.so\, perl.c\, line 1594   main perl\, perlmain.c\, line 85   __start perl

This block at address 0x140045710 was allocated at​:   malloc libc.so   Perl_safesysmalloc libperl.so\, util.c\, line 78   S_more_xrv libperl.so\, sv.c\, line 692   S_new_xrv libperl.so\, sv.c\, line 666   Perl_sv_upgrade libperl.so\, sv.c\, line 1354   S_refto libperl.so\, pp.c\, line 482   Perl_pp_refgen libperl.so\, pp.c\, line 446   Perl_runops_debug libperl.so\, dump.c\, line 1398   S_call_body libperl.so\, perl.c\, line 2037   Perl_call_sv libperl.so\, perl.c\, line 1955   S_call_list_body libperl.so\, perl.c\, line 4053   Perl_call_list libperl.so\, perl.c\, line 3981   Perl_newATTRSUB libperl.so\, op.c\, line 5100   Perl_yyparse libperl.so\, perly.c\, line 1600   S_parse_body libperl.so\, perl.c\, line 1513   perl_parse libperl.so\, perl.c\, line 1040   main perl\, perlmain.c\, line 83   __start perl

-- $jhi++; # http​://www.iki.fi/jhi/   # There is this special biologist word we use for 'stable'.   # It is 'dead'. -- Jack Cohen

p5pRT commented 22 years ago

From @jhi

On Sun\, May 26\, 2002 at 05​:06​:54AM +0300\, Jarkko Hietaniemi wrote​:

Using your simpler version\, 16792\, and Third Degree I get this before the core dump​:

With the longer version the same heap violation but the script runs a little bit further before barfing.

---------------------------------------------------------------- fih -- 7 -- util.c​: 151​: freeing heap at byte 32 of 1008-byte block free libc.so Perl_safesysfree libperl.so\, util.c\, line 151 Perl_sv_setsv_flags libperl.so\, sv.c\, line 3911 Perl_pp_sassign libperl.so\, pp_hot.c\, line 108 Perl_runops_debug libperl.so\, dump.c\, line 1398 S_run_body libperl.so\, perl.c\, line 1673 perl_run libperl.so\, perl.c\, line 1594 main perl\, perlmain.c\, line 85 __start perl

This block at address 0x140045710 was allocated at​: malloc libc.so Perl_safesysmalloc libperl.so\, util.c\, line 78 S_more_xrv libperl.so\, sv.c\, line 692 S_new_xrv libperl.so\, sv.c\, line 666 Perl_sv_upgrade libperl.so\, sv.c\, line 1354 S_refto libperl.so\, pp.c\, line 482 Perl_pp_refgen libperl.so\, pp.c\, line 446 Perl_runops_debug libperl.so\, dump.c\, line 1398 S_call_body libperl.so\, perl.c\, line 2037 Perl_call_sv libperl.so\, perl.c\, line 1955 S_call_list_body libperl.so\, perl.c\, line 4053 Perl_call_list libperl.so\, perl.c\, line 3981 Perl_newATTRSUB libperl.so\, op.c\, line 5100 Perl_yyparse libperl.so\, perly.c\, line 1600 S_parse_body libperl.so\, perl.c\, line 1513 perl_parse libperl.so\, perl.c\, line 1040 main perl\, perlmain.c\, line 83 __start perl

-- $jhi++; # http​://www.iki.fi/jhi/ # There is this special biologist word we use for 'stable'. # It is 'dead'. -- Jack Cohen

-- $jhi++; # http​://www.iki.fi/jhi/   # There is this special biologist word we use for 'stable'.   # It is 'dead'. -- Jack Cohen

p5pRT commented 22 years ago

From @jhi

This is the minimum I can in Tru64 whittle it down and still get the mid-heap free seen by Third Degree (notes​: $SIG{__WARN__} is now outside of a BEGIN\, while loop has been unrolled\, the $expect has been simplified (it still needs to be a "")\, the constant scalar ref has been simplified.

Note that I get SIGILL\, not SIGSEGV. If I remove the $SIG{__WARN__} bit\, no more the mid-heap free\, but I still do get the SIGILL.

#!./perl -w

my @​warnings;

BEGIN {   push @​warnings\, \'FOO'; }

$SIG{__WARN__} = sub {   push @​warnings\, $_[0]; };

my $instruction; my $expect;

$instruction = shift @​warnings; $instruction = $$instruction; $expect = "\"$instruction\""; $instruction = shift @​warnings;

__END__

-- $jhi++; # http​://www.iki.fi/jhi/   # There is this special biologist word we use for 'stable'.   # It is 'dead'. -- Jack Cohen

p5pRT commented 22 years ago

From @jhi

And here's Purify trace for my cut-down version​:

**** Purify instrumented pureperl (pid 22849 at Sun May 26 11​:49​:30 2002)   * Purify 2001a.04.00 Solaris 2 (32-bit) Copyright (C) 1992-2001 Rational Software Corp. All rights reserved.
  * For contact information type​: "purify -help"   * Options settings​: -chain-length=25 -windows=no -log-file=perl.log \   -append-logfile=yes -purify \   -purify-home=/usr/common/apps/purify/releases/purify.sol.2001a.04.00   * License successfully checked out.   * Command-line​: pureperl -Ilib /home/jpl/pt

**** Purify instrumented pureperl (pid 22849) **** FMR​: Free memory read​:   * This is occurring while in​:   Perl_sv_clear [sv.c​:4984]   Perl_sv_free [sv.c​:5188]   Perl_av_undef [av.c​:455]   Perl_sv_clear [sv.c​:5016]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4409]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4388]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_sv_clear [sv.c​:5042]   Perl_sv_free [sv.c​:5188]   Perl_hv_free_ent [hv.c​:1594]   S_hfreeentries [hv.c​:1681]   Perl_hv_undef [hv.c​:1707]   Perl_sv_clear [sv.c​:5013]   Perl_sv_free [sv.c​:5188]   Perl_gp_free [gv.c​:1247]   Perl_sv_clear [sv.c​:5022]   Perl_sv_free [sv.c​:5188]   do_clean_all [sv.c​:400]   S_visit [sv.c​:292]   Perl_sv_clean_all [sv.c​:418]   perl_destruct [perl.c​:770]   * Reading 4 bytes from 0x3ca3e0 in the heap.   * Address 0x3ca3e0 is 21 bytes past end of a freed block at 0x3ca3c8 of 4 bytes.   * This block was allocated from​:   malloc [rtlib.o]   Perl_safesysmalloc [util.c​:78]   Perl_sv_upgrade [sv.c​:1354]   Perl_sv_setsv_flags [sv.c​:3627]   Perl_pp_sassign [pp_hot.c​:108]   Perl_runops_debug [dump.c​:1398]   S_run_body [perl.c​:1673]   perl_run [perl.c​:1594]   main [perlmain.c​:85]   _start [crt1.o]   * There have been 687 frees since this block was freed.

**** Purify instrumented pureperl (pid 22849) **** FMR​: Free memory read​:   * This is occurring while in​:   Perl_mg_free [mg.c​:351]   Perl_sv_clear [sv.c​:4985]   Perl_sv_free [sv.c​:5188]   Perl_av_undef [av.c​:455]   Perl_sv_clear [sv.c​:5016]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4409]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4388]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_sv_clear [sv.c​:5042]   Perl_sv_free [sv.c​:5188]   Perl_hv_free_ent [hv.c​:1594]   S_hfreeentries [hv.c​:1681]   Perl_hv_undef [hv.c​:1707]   Perl_sv_clear [sv.c​:5013]   Perl_sv_free [sv.c​:5188]   Perl_gp_free [gv.c​:1247]   Perl_sv_clear [sv.c​:5022]   Perl_sv_free [sv.c​:5188]   do_clean_all [sv.c​:400]   S_visit [sv.c​:292]   Perl_sv_clean_all [sv.c​:418]   * Reading 4 bytes from 0x3ca3e0 in the heap.   * Address 0x3ca3e0 is 21 bytes past end of a freed block at 0x3ca3c8 of 4 bytes.   * This block was allocated from​:   malloc [rtlib.o]   Perl_safesysmalloc [util.c​:78]   Perl_sv_upgrade [sv.c​:1354]   Perl_sv_setsv_flags [sv.c​:3627]   Perl_pp_sassign [pp_hot.c​:108]   Perl_runops_debug [dump.c​:1398]   S_run_body [perl.c​:1673]   perl_run [perl.c​:1594]   main [perlmain.c​:85]   _start [crt1.o]   * There have been 687 frees since this block was freed.

**** Purify instrumented pureperl (pid 22849) **** MSE​: Memory segment error​:   * This is occurring while in​:   Perl_mg_free [mg.c​:354]   Perl_sv_clear [sv.c​:4985]   Perl_sv_free [sv.c​:5188]   Perl_av_undef [av.c​:455]   Perl_sv_clear [sv.c​:5016]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4409]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4388]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_sv_clear [sv.c​:5042]   Perl_sv_free [sv.c​:5188]   Perl_hv_free_ent [hv.c​:1594]   S_hfreeentries [hv.c​:1681]   Perl_hv_undef [hv.c​:1707]   Perl_sv_clear [sv.c​:5013]   Perl_sv_free [sv.c​:5188]   Perl_gp_free [gv.c​:1247]   Perl_sv_clear [sv.c​:5022]   Perl_sv_free [sv.c​:5188]   do_clean_all [sv.c​:400]   S_visit [sv.c​:292]   Perl_sv_clean_all [sv.c​:418]   * Accessing a memory range that crosses a memory segment boundary.   Addressing 0x1000010 for 4 bytes ending at 0x1000014\,   which is neither in the heap nor the main stack.

**** Purify instrumented pureperl (pid 22849) **** COR​: Fatal core dump​:   * This is occurring while in​:   Perl_mg_free [mg.c​:354]   Perl_sv_clear [sv.c​:4985]   Perl_sv_free [sv.c​:5188]   Perl_av_undef [av.c​:455]   Perl_sv_clear [sv.c​:5016]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4409]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_cv_undef [op.c​:4388]   Perl_sv_clear [sv.c​:5010]   Perl_sv_free [sv.c​:5188]   Perl_sv_clear [sv.c​:5042]   Perl_sv_free [sv.c​:5188]   Perl_hv_free_ent [hv.c​:1594]   S_hfreeentries [hv.c​:1681]   Perl_hv_undef [hv.c​:1707]   Perl_sv_clear [sv.c​:5013]   Perl_sv_free [sv.c​:5188]   Perl_gp_free [gv.c​:1247]   Perl_sv_clear [sv.c​:5022]   Perl_sv_free [sv.c​:5188]   do_clean_all [sv.c​:400]   S_visit [sv.c​:292]   Perl_sv_clean_all [sv.c​:418]   * Received signal 11 (SIGSEGV - Segmentation Fault)   * Faulting address = 0x1000010   * Signal mask​: (SIGSEGV)   * Pending signals​:

-- $jhi++; # http​://www.iki.fi/jhi/   # There is this special biologist word we use for 'stable'.   # It is 'dead'. -- Jack Cohen

p5pRT commented 22 years ago

From @nwc10

I have Jarkko's test case down to​:

#!./perl

my @​warnings;

BEGIN {   push @​warnings\, \'FOO'; }

my $instruction = shift @​warnings; $instruction = $$instruction;

Sadly it causes a the `impossible' happened bug in valgrind\, so I'm at a bit of a loss. However\, with this version the SEGV happens in global destruction. So\, if I change the above to this​:

SV = RV(0x817a1ac) at 0x81791dc   REFCNT = 1   FLAGS = (PADMY\,ROK)   RV = 0x8179230   SV = PV(0x816aaf4) at 0x8179230   REFCNT = 1   FLAGS = (POK\,READONLY\,pPOK)   PV = 0x816e338 "FOO"\0   CUR = 3   LEN = 4 SV = UNKNOWN(0xff) (0x817a1ac) at 0x81791dc   REFCNT = 1   FLAGS = (PADMY)

but if I take the BEGIN block out no SEGV\, and I see this​:

SV = RV(0x8179f34) at 0x81791f4   REFCNT = 1   FLAGS = (PADMY\,ROK)   RV = 0x81791dc   SV = PV(0x816aadc) at 0x81791dc   REFCNT = 2   FLAGS = (POK\,READONLY\,pPOK)   PV = 0x8179e70 "FOO"\0   CUR = 3   LEN = 4 SV = PV(0x816aa10) at 0x81791f4   REFCNT = 1   FLAGS = (PADMY\,POK\,pPOK)   PV = 0x81805e0 "FOO"\0   CUR = 3   LEN = 4

What is so special about the BEGIN block that it is trashing the stack\, and causing a later SEGV?

(this is still a bug in current blead)

Nicholas Clark

p5pRT commented 22 years ago

From @nwc10

OK. I found this via the RT web interface\, and added a comment there\, but it's not yet shown up here despite the fact that I added perl5-porters@​perl.org as a CC

On Sun\, May 26\, 2002 at 05​:46​:06AM +0300\, Jarkko Hietaniemi wrote​:

#!./perl -w

my @​warnings;

BEGIN { push @​warnings\, \'FOO'; }

$SIG{__WARN__} = sub { push @​warnings\, $_[0]; };

my $instruction; my $expect;

$instruction = shift @​warnings; $instruction = $$instruction; $expect = "\"$instruction\""; $instruction = shift @​warnings;

__END__

I have Jarkko's test case down to​:

#!./perl

my @​warnings;

BEGIN {   push @​warnings\, \'FOO'; }

my $instruction = shift @​warnings; $instruction = $$instruction;

Sadly it causes a the `impossible' happened bug in valgrind\, so I'm at a bit of a loss. However\, with this version the SEGV happens in global destruction. So\, if I change the above to Devel​::Peek​::Dump $instruction before and after the last line\, I see​:

SV = RV(0x817a1ac) at 0x81791dc   REFCNT = 1   FLAGS = (PADMY\,ROK)   RV = 0x8179230   SV = PV(0x816aaf4) at 0x8179230   REFCNT = 1   FLAGS = (POK\,READONLY\,pPOK)   PV = 0x816e338 "FOO"\0   CUR = 3   LEN = 4 SV = UNKNOWN(0xff) (0x817a1ac) at 0x81791dc   REFCNT = 1   FLAGS = (PADMY)

but if I take the BEGIN block out no SEGV\, and I see this​:

SV = RV(0x8179f34) at 0x81791f4   REFCNT = 1   FLAGS = (PADMY\,ROK)   RV = 0x81791dc   SV = PV(0x816aadc) at 0x81791dc   REFCNT = 2   FLAGS = (POK\,READONLY\,pPOK)   PV = 0x8179e70 "FOO"\0   CUR = 3   LEN = 4 SV = PV(0x816aa10) at 0x81791f4   REFCNT = 1   FLAGS = (PADMY\,POK\,pPOK)   PV = 0x81805e0 "FOO"\0   CUR = 3   LEN = 4

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)

Nicholas Clark

p5pRT commented 22 years ago

From @iabyn

On Fri\, Jan 17\, 2003 at 09​:46​:40PM +0000\, Nicholas Clark wrote​:

I have Jarkko's test case down to​:

#!./perl

my @​warnings;

BEGIN { push @​warnings\, \'FOO'; }

my $instruction = shift @​warnings; $instruction = $$instruction;

It reduces to the even simpler case​:

  my $y;   BEGIN { $y=\1; }   $y = $$y;

What is happening is that $y is an RV pointing to an IV(1).

Initially the IV has a refcnt of 2 (one pointer from $y\, the other pointerr in its capacity as a constant\, either in BEGIN's pad or BEGIN's const op (depending on threading)).

After the BEGIN CV has finished executing and is undef'ed\, the pad/opconst pointer is thrown away\, and the IV(1) is left with a refcnt of 1\, *and* it is SvREADONLY.

When the $y = $$y is done\, the following is called​:

  Perl_sv_setsv_flags(dstr=RV(IV(1))\, sstr=IV(1)) is called\,

which does

  SV_CHECK_THINKFIRST_COW_DROP(dstr);

which does

  if (SvROK(sv))   sv_unref_flags(sv\, flags);

which does

  if (SvREFCNT(rv) != 1 || SvREADONLY(rv) || (flags & SV_IMMEDIATE_UNREF))   SvREFCNT_dec(rv);

which blows away the IV(1)\, which eventually leaves $y as a freed SV​:

as shown by the output of -DstR​:

  (/tmp/p​:5) padsv($y)   => \IV(1)
  (/tmp/p​:5) rv2sv   => IV(1)
  (/tmp/p​:5) padsv($y)   => IV(1) \IV(1)
  (/tmp/p​:5) sassign   => FREED

So it's a combination of the fact that the IV is SvREADONLY (because its a constant)\, and it has refcnt of 1 (because it appeared in a BEGIN).

That's what the problem is\, I haven't a clue what the solution is.

Dave.

-- "You're so sadly neglected\, and often ignored. A poor second to Belgium\, When going abroad." Monty Python - "Finland"

p5pRT commented 22 years ago

From @nwc10

On Fri\, Jan 17\, 2003 at 09​:46​:40PM +0000\, Nicholas Clark wrote​:

I have Jarkko's test case down to​:

#!./perl

my @​warnings;

BEGIN { 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   FLAGS = (ROK)   RV = 0x817386c   SV = PV(0x8164580) at 0x817386c   REFCNT = 1   FLAGS = (POK\,READONLY\,pPOK)   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)

Answer​:

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   FLAGS = (PADBUSY\,PADMY\,ROK)   RV = 0x8173c6c (gdb) call Perl_sv_dump(sstr) SV = PV(0x8164550) at 0x8173c6c   REFCNT = 1   FLAGS = (POK\,READONLY\,pPOK)   PV = 0x8163ef0 "Foo"\0   CUR = 3   LEN = 4 (gdb) step 3574 SV_CHECK_THINKFIRST_COW_DROP(dstr); (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))   SvREFCNT_dec(rv);   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   FLAGS = (PADBUSY\,PADMY\,ROK)   RV = 0x81641ec   SV = PVIV(0x8164820) at 0x81641ec   REFCNT = 1   FLAGS = (POK\,FAKE\,READONLY\,pPOK)   IV = -1271882440   PV = 0x81630f8 "perl"   CUR = 4   LEN = 0 SV = UNKNOWN(0xff) (0x817ec28) at 0x81738b4   REFCNT = 1   FLAGS = (PADBUSY\,PADMY) Bus error (core dumped)

I think that the READONLY test should go. Any reason why it must stay?

Nicholas Clark

p5pRT commented 22 years ago

From @nwc10

On Fri\, Jan 17\, 2003 at 11​:25​:34PM -0000\, Dave Mitchell wrote​:

It reduces to the even simpler case​:

my $y;
BEGIN \{ $y=\\1; \}
$y = $$y;

That's what the problem is\, I haven't a clue what the solution is.

Thanks. Offline I've just worked out the same thing. :-)

Nicholas Clark

p5pRT commented 22 years ago

From nick@ing-simmons.net

Nicholas Clark \nick@​unfortu\.net writes​:

I think that the READONLY test should go. Any reason why it must stay?

Can't see one so far.

Nicholas Clark -- Nick Ing-Simmons http​://www.ni-s.u-net.com/

p5pRT commented 22 years ago

From @nwc10

On Sat\, Jan 18\, 2003 at 06​:07​:14PM +0000\, Nick Ing-Simmons wrote​:

Nicholas Clark \nick@​unfortu\.net writes​:

I think that the READONLY test should go. Any reason why it must stay?

Can't see one so far.

This passes all tests for a normal\, and ithreads with -DPERL_COPY_OR_WRITE -DDEBUGGING

Nicholas Clark

Inline Patch ```diff --- sv.c.orig Thu Jan 16 21:45:54 2003 +++ sv.c Mon Jan 20 20:49:55 2003 @@ -7616,7 +7616,9 @@ Perl_sv_unref_flags(pTHX_ SV *sv, U32 fl } SvRV(sv) = 0; SvROK_off(sv); - if (SvREFCNT(rv) != 1 || SvREADONLY(rv) || (flags & SV_IMMEDIATE_UNREF)) + /* You can't have a || SvREADONLY(rv) here, as $a = $$a, where $a was + assigned to as BEGIN {$a = \"Foo"} will fail. */ + if (SvREFCNT(rv) != 1 || (flags & SV_IMMEDIATE_UNREF)) SvREFCNT_dec(rv); else /* XXX Hack, but hard to make $a=$a->[1] work otherwise */ sv_2mortal(rv); /* Schedule for freeing later */ --- t/op/ref.t.orig Mon Aug 12 13:48:39 2002 +++ t/op/ref.t Mon Jan 20 21:14:10 2003 @@ -5,7 +5,7 @@ BEGIN { @INC = qw(. ../lib); } -print "1..63\n"; +print "1..65\n"; require 'test.pl'; @@ -296,23 +296,44 @@ $a = $a->[1]; print "not " unless $a == 2; print "ok 55\n"; -sub x::DESTROY {print "ok ", 55 + shift->[0], "\n"} -{ my $a1 = bless [4],"x"; - my $a2 = bless [3],"x"; - { my $a3 = bless [2],"x"; - my $a4 = bless [1],"x"; - 567; +# This test used to coredump. The BEGIN block is important as it causes the +# op that created the constant reference to be freed. Hence the only +# reference to the constant string "pass" is in $a. The hack that made +# sure $a = $a->[1] would work didn't work with references to constants. + +my $test = 56; + +foreach my $lexical ('', 'my $a; ') { + my $expect = "pass\n"; + my $result = runperl (switches => ['-wl'], stderr => 1, + prog => $lexical . 'BEGIN {$a = \q{pass}}; $a = $$a; print $a'); + + if ($? == 0 and $result eq $expect) { + print "ok $test\n"; + } else { + print "not ok $test # \$? = $?\n"; + print "# expected ", _qq ($expect), ", got ", _qq ($result), "\n"; } + $test++; } +sub x::DESTROY {print "ok ", $test + shift->[0], "\n"} +{ my $a1 = bless [3],"x"; + my $a2 = bless [2],"x"; + { my $a3 = bless [1],"x"; + my $a4 = bless [0],"x"; + 567; + } +} +$test+=4; my $result = runperl (switches=>['-l'], prog=> 'print 1; print qq-*$\*-;print 1;'); my $expect = "1\n*\n*\n1\n"; if ($result eq $expect) { - print "ok 60\n"; + print "ok $test\n"; } else { - print "not ok 60\n"; + print "not ok $test\n"; foreach ($expect, $result) { s/\n/\\n/gs; } @@ -321,7 +342,7 @@ if ($result eq $expect) { # test global destruction -my $test = 61; +++$test; my $test1 = $test + 1; my $test2 = $test + 2; ```
p5pRT commented 21 years ago

From @iabyn

Presumably the patch fixed this bug\, so I'm marking it as resolved.

Dave.

p5pRT commented 21 years ago

@iabyn - Status changed from 'open' to 'resolved'