Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault in Perl_runops_standard in scalar access after some typeglob witchery #14862

Closed
p5pRT opened this issue Aug 19, 2015 · 16 comments
Closed

Comments

@p5pRT
Copy link

p5pRT commented Aug 19, 2015

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

Searchable as RT125840$

@p5pRT
Copy link
Author

p5pRT commented Aug 19, 2015

From @dcollinsn

Greetings Porters,

I have compiled bleadperl with the afl-gcc compiler using​:

./Configure -Dusedevel -Dprefix='/usr/local/perl-afl' -Dcc=afl-gcc -Duselongdouble -Duse64bitint -Doptimize=-g -des
AFL_HARDEN=1 make

And then fuzzed the resulting binary using​:

AFL_NO_VAR_CHECK=1 afl-fuzz -i in -o out bin/perl @​@​

After reducing testcases using `afl-tmin` and filtering out testcases that are merely iterations of "#!perl -u", I have located the following testcase that triggers a segmentation fault in the perl interpreter. The testcase reduced automatically to​:

$x=*0;0!=0;$0=*x=$x

And further reduced manually to​:

$x=*0;$0=*x=$x

Some more crashing examples that may better illustrate what's going on...

$x=*0;*x=$x;$0=*x
*x=$x=*a;$a=$x
*x=$x=*a;$x (only on some perls, probably the $x access gets optimized out)

This testcase emits no warnings nor errors, but causes both perl and miniperl to crash with a segmentation fault. This is a long-standing crash, reproducible back to 5.6.0, which is the earliest version that compiles on my system. Bisect not attempted.

****Debug results for "$x=*0;$0=*x=$x"****

**GDB**

GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+​: GNU GPL version 3 or later <http​://gnu.org/licenses/gpl.html>
This is free software​: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see​:
<http​://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/dcollins/perl/miniperl...done.
(gdb) run
Starting program​: /home/dcollins/perl/miniperl /usr/local/perl-afl/out/allcrash/f2i000000
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
Perl_gv_efullname4 (sv=0x871ae9c, gv=0x871aca8, prefix=0x86dd612 "*",
  keepmain=true) at gv.c​:2394
2394 const GV * const egv = GvEGVx(gv);
(gdb) bt
#0 Perl_gv_efullname4 (sv=0x871ae9c, gv=0x871aca8, prefix=0x86dd612 "*",
  keepmain=true) at gv.c​:2394
#1 0x083b3102 in S_sv_unglob (sv=0x871aca8, flags=0) at sv.c​:10152
#2 Perl_sv_force_normal_flags (sv=0x871aca8, flags=0) at sv.c​:5162
#3 0x083b3f5c in Perl_sv_pvn_force_flags (sv=0x871aca8, lp=0xbffff4f8,
  flags=2) at sv.c​:9688
#4 0x082e3993 in Perl_magic_set (sv=0x871aca8, mg=0x8719b20) at mg.c​:3147
#5 0x082df4ad in Perl_mg_set (sv=0x871aca8) at mg.c​:277
#6 0x083563b0 in Perl_pp_sassign () at pp_hot.c​:225
#7 0x0833de9b in Perl_runops_standard () at run.c​:41
#8 0x0805ec19 in S_run_body (my_perl=0x8708008) at perl.c​:2448
#9 perl_run (my_perl=0x8708008) at perl.c​:2371
#10 0x080f2bbe in main (argc=2, argv=0xbffff7d4, env=0xbffff7e0)
  at miniperlmain.c​:122
(gdb) l
2389 }
2390
2391 void
2392 Perl_gv_efullname4(pTHX_ SV *sv, const GV *gv, const char *prefix, bool keepmain)
2393 {
2394 const GV * const egv = GvEGVx(gv);
2395
2396 PERL_ARGS_ASSERT_GV_EFULLNAME4;
2397
2398 gv_fullname4(sv, egv ? egv : gv, prefix, keepmain);

**VALGRIND**

==30968== Memcheck, a memory error detector
==30968== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30968== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==30968== Command​: ./miniperl /usr/local/perl-afl/out/allcrash/f2i000000
==30968==
==30968== Invalid read of size 4
==30968== at 0x80F42F2​: Perl_gv_efullname4 (gv.c​:2394)
==30968== by 0x83B3101​: Perl_sv_force_normal_flags (sv.c​:10152)
==30968== by 0x83B3F5B​: Perl_sv_pvn_force_flags (sv.c​:9688)
==30968== by 0x82E3992​: Perl_magic_set (mg.c​:3147)
==30968== by 0x82DF4AC​: Perl_mg_set (mg.c​:277)
==30968== by 0x83563AF​: Perl_pp_sassign (pp_hot.c​:225)
==30968== by 0x833DE9A​: Perl_runops_standard (run.c​:41)
==30968== by 0x805EC18​: perl_run (perl.c​:2448)
==30968== by 0x80F2BBD​: main (miniperlmain.c​:122)
==30968== Address 0x20 is not stack'd, malloc'd or (recently) free'd
==30968==
==30968==
==30968== Process terminating with default action of signal 11 (SIGSEGV)
==30968== Access not within mapped region at address 0x20
==30968== at 0x80F42F2​: Perl_gv_efullname4 (gv.c​:2394)
==30968== by 0x83B3101​: Perl_sv_force_normal_flags (sv.c​:10152)
==30968== by 0x83B3F5B​: Perl_sv_pvn_force_flags (sv.c​:9688)
==30968== by 0x82E3992​: Perl_magic_set (mg.c​:3147)
==30968== by 0x82DF4AC​: Perl_mg_set (mg.c​:277)
==30968== by 0x83563AF​: Perl_pp_sassign (pp_hot.c​:225)
==30968== by 0x833DE9A​: Perl_runops_standard (run.c​:41)
==30968== by 0x805EC18​: perl_run (perl.c​:2448)
==30968== by 0x80F2BBD​: main (miniperlmain.c​:122)
==30968== If you believe this happened as a result of a stack
==30968== overflow in your program's main thread (unlikely but
==30968== possible), you can try to increase the size of the
==30968== main thread stack using the --main-stacksize= flag.
==30968== The main thread stack size used in this run was 8388608.
==30968==
==30968== HEAP SUMMARY​:
==30968== in use at exit​: 87,353 bytes in 526 blocks
==30968== total heap usage​: 626 allocs, 100 frees, 104,870 bytes allocated
==30968==
==30968== LEAK SUMMARY​:
==30968== definitely lost​: 96 bytes in 1 blocks
==30968== indirectly lost​: 1,951 bytes in 22 blocks
==30968== possibly lost​: 11,781 bytes in 265 blocks
==30968== still reachable​: 73,525 bytes in 238 blocks
==30968== suppressed​: 0 bytes in 0 blocks
==30968== Rerun with --leak-check=full to see details of leaked memory
==30968==
==30968== For counts of detected and suppressed errors, rerun with​: -v
==30968== ERROR SUMMARY​: 1 errors from 1 contexts (suppressed​: 25 from 8)
Segmentation fault

****Debug results for "*x=$x=*a;$a=$x"****

**GDB**

GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+​: GNU GPL version 3 or later <http​://gnu.org/licenses/gpl.html>
This is free software​: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see​:
<http​://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/dcollins/perl/miniperl...done.
(gdb) run
Starting program​: /home/dcollins/perl/miniperl -e \*x=\$x=\*a\;\$a=\$x
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x08356d65 in Perl_pp_gvsv () at pp_hot.c​:64
64 PUSHs(GvSVn(cGVOP_gv));
(gdb) bt
#0 0x08356d65 in Perl_pp_gvsv () at pp_hot.c​:64
#1 0x0833de9b in Perl_runops_standard () at run.c​:41
#2 0x0805ec19 in S_run_body (my_perl=0x8708008) at perl.c​:2448
#3 perl_run (my_perl=0x8708008) at perl.c​:2371
#4 0x080f2bbe in main (argc=3, argv=0xbffff7f4, env=0xbffff804)
  at miniperlmain.c​:122
(gdb) l
59 dSP;
60 EXTEND(SP,1);
61 if (UNLIKELY(PL_op->op_private & OPpLVAL_INTRO))
62 PUSHs(save_scalar(cGVOP_gv));
63 else
64 PUSHs(GvSVn(cGVOP_gv));
65 RETURN;
66 }
67
68
(gdb)

**VALGRIND**

==3636== Memcheck, a memory error detector
==3636== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==3636== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==3636== Command​: ./miniperl -e *x=$x=*a;$a=$x
==3636==
==3636== Invalid read of size 4
==3636== at 0x8356D65​: Perl_pp_gvsv (pp_hot.c​:64)
==3636== by 0x833DE9A​: Perl_runops_standard (run.c​:41)
==3636== by 0x805EC18​: perl_run (perl.c​:2448)
==3636== by 0x80F2BBD​: main (miniperlmain.c​:122)
==3636== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==3636==
==3636==
==3636== Process terminating with default action of signal 11 (SIGSEGV)
==3636== Access not within mapped region at address 0x0
==3636== at 0x8356D65​: Perl_pp_gvsv (pp_hot.c​:64)
==3636== by 0x833DE9A​: Perl_runops_standard (run.c​:41)
==3636== by 0x805EC18​: perl_run (perl.c​:2448)
==3636== by 0x80F2BBD​: main (miniperlmain.c​:122)
==3636== If you believe this happened as a result of a stack
==3636== overflow in your program's main thread (unlikely but
==3636== possible), you can try to increase the size of the
==3636== main thread stack using the --main-stacksize= flag.
==3636== The main thread stack size used in this run was 8388608.
==3636==
==3636== HEAP SUMMARY​:
==3636== in use at exit​: 87,458 bytes in 535 blocks
==3636== total heap usage​: 636 allocs, 101 frees, 96,704 bytes allocated
==3636==
==3636== LEAK SUMMARY​:
==3636== definitely lost​: 96 bytes in 1 blocks
==3636== indirectly lost​: 1,951 bytes in 22 blocks
==3636== possibly lost​: 11,720 bytes in 267 blocks
==3636== still reachable​: 73,691 bytes in 245 blocks
==3636== suppressed​: 0 bytes in 0 blocks
==3636== Rerun with --leak-check=full to see details of leaked memory
==3636==
==3636== For counts of detected and suppressed errors, rerun with​: -v
==3636== ERROR SUMMARY​: 1 errors from 1 contexts (suppressed​: 25 from 8)
Segmentation fault

**PERL -V**

Summary of my perl5 (revision 5 version 23 subversion 2) configuration​:
  Commit id​: 9cd8e8a
  Platform​:
  osname=linux, osvers=2.6.32-5-686, archname=i686-linux-64int-ld
  uname='linux nagios 2.6.32-5-686 #1 smp tue may 13 16​:33​:32 utc 2014 i686 gnulinux '
  config_args='-Dusedevel -Dprefix=/usr/local/perl-afl -Dcc=afl-gcc -Duselongdouble -Duse64bitint -Doptimize=-g -des'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=undef, usemultiplicity=undef
  use64bitint=define, use64bitall=undef, uselongdouble=define
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='afl-gcc', ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-g',
  cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
  ccversion='', gccversion='4.4.5', gccosandvers=''
  intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=12345678, doublekind=3
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12, longdblkind=3
  ivtype='long long', ivsize=8, nvtype='long double', nvsize=12, Off_t='off_t', lseeksize=8
  alignbytes=4, prototype=define
  Linker and Libraries​:
  ld='afl-gcc', ldflags =' -fstack-protector -L/usr/local/lib'
  libpth=/usr/local/lib /usr/lib/gcc/i486-linux-gnu/4.4.5/include-fixed /usr/lib /lib/../lib /usr/lib/../lib /lib /usr/lib/i486-linux-gnu /usr/lib64
  libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
  perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
  libc=libc-2.11.3.so, so=so, useshrplib=false, libperl=libperl.a
  gnulibc_version='2.11.3'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
  cccdlflags='-fPIC', lddlflags='-shared -g -L/usr/local/lib -fstack-protector'

Characteristics of this binary (from libperl)​:
  Compile-time options​: HAS_TIMES PERLIO_LAYERS PERL_COPY_ON_WRITE
  PERL_DONT_CREATE_GVSV PERL_EXTERNAL_GLOB
  PERL_HASH_FUNC_ONE_AT_A_TIME_HARD PERL_IS_MINIPERL
  PERL_MALLOC_WRAP PERL_PRESERVE_IVUV PERL_USE_DEVEL
  USE_64_BIT_INT USE_LARGE_FILES USE_LOCALE
  USE_LOCALE_COLLATE USE_LOCALE_CTYPE
  USE_LOCALE_NUMERIC USE_LOCALE_TIME USE_LONG_DOUBLE
  USE_PERLIO USE_PERL_ATOF USE_SITECUSTOMIZE
  Built under linux
  Compiled at Aug 18 2015 21​:45​:40
  @​INC​:
  /home/dcollins/perl/cpan/AutoLoader/lib
  /home/dcollins/perl/dist/Carp/lib
  /home/dcollins/perl/dist/PathTools
  /home/dcollins/perl/dist/PathTools/lib
  /home/dcollins/perl/cpan/ExtUtils-Command/lib
  /home/dcollins/perl/cpan/ExtUtils-Install/lib
  /home/dcollins/perl/cpan/ExtUtils-MakeMaker/lib
  /home/dcollins/perl/cpan/ExtUtils-Manifest/lib
  /home/dcollins/perl/cpan/File-Path/lib
  /home/dcollins/perl/ext/re
  /home/dcollins/perl/dist/Term-ReadLine/lib
  /home/dcollins/perl/dist/Exporter/lib
  /home/dcollins/perl/ext/File-Find/lib
  /home/dcollins/perl/cpan/Text-Tabs/lib
  /home/dcollins/perl/dist/constant/lib
  /home/dcollins/perl/lib
  .

@p5pRT
Copy link
Author

p5pRT commented Aug 19, 2015

From @hvds

On Tue Aug 18 19​:25​:20 2015, dcollinsn@​gmail.com wrote​:

[...] The testcase reduced automatically to​:

$x=*0;0!=0;$0=*x=$x

Thanks for the report, this was previously reported (also by fuzzers) as RTs 123710, 123804 and 123997; it is currently perceived as unfixable until we address RT 77706 (refcount the stack).

Hugo

@p5pRT
Copy link
Author

p5pRT commented Aug 19, 2015

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2015

From @rurban

On Wed Aug 19 12​:10​:00 2015, hv wrote​:

On Tue Aug 18 19​:25​:20 2015, dcollinsn@​gmail.com wrote​:

[...] The testcase reduced automatically to​:

$x=*0;0!=0;$0=*x=$x

Thanks for the report, this was previously reported (also by fuzzers)
as RTs 123710, 123804 and 123997; it is currently perceived as
unfixable until we address RT 77706 (refcount the stack).

I cannot follow this argumentation.
$x=*0;*x=$x fails because the lhs references the rhs,
which is deleted in S_glob_assign_glob with​:
gp_free(MUTABLE_GV(dstr));

so the simple fix is

  gp_free(MUTABLE_GV(dstr));
  GvINTRO_off(dstr); /* one-shot flag */
  if (!SvIS_FREED(sstr)) /* dstr could have referenced sstr */
  GvGP_set(dstr, gp_ref(GvGP(sstr)));

(sans the test for $x=*0;*x=$x)

Furthermore, the stack *IS* refcounted, even empty PADTMP entries.
Do you mean you want to refcount PL_curpad? There's no problem with curpad here.
--
Reini Urban

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2015

From @rurban

Attached patch fixes all these glob assigns where the lhs references the rhs.

--
Reini Urban

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2015

From @rurban

0001-fix-perl-125840-x-0-x-x.patch
From e2c51c2e8787f9f6edf1f4c4a5e01b132f7f759f Mon Sep 17 00:00:00 2001
From: Reini Urban <rurban@cpanel.net>
Date: Tue, 25 Aug 2015 12:53:08 +0200
Subject: [PATCH] fix perl #125840: $x=*0; *x=$x

In this case the lhs of the glob references the rhs, and when
clearing the lhs before the copy of the GP values, the rhs was deleted.
Protect from that.

Furthermore, the result needs to be assigned a fresh GP to fix
the case with -e'*x=$x=*a;$a=$x'

fixup glob_assign_glob
---
 sv.c      | 8 ++++++--
 t/op/gv.t | 7 ++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git sv.c sv.c
index 906518c..0d3dbb8 100644
--- sv.c
+++ sv.c
@@ -3974,8 +3974,12 @@ S_glob_assign_glob(pTHX_ SV *const dstr, SV *const sstr, const int dtype)
     }
 
     gp_free(MUTABLE_GV(dstr));
-    GvINTRO_off(dstr);		/* one-shot flag */
-    GvGP_set(dstr, gp_ref(GvGP(sstr)));
+    GvINTRO_off(dstr);		 /* one-shot flag */
+    if (SvIS_FREED(sstr)) {      /* dstr could have referenced sstr */
+        GvGP_set(dstr, Perl_newGP(aTHX_ MUTABLE_GV(dstr)));
+    } else {
+        GvGP_set(dstr, gp_ref(GvGP(sstr)));
+    }
     if (SvTAINTED(sstr))
 	SvTAINT(dstr);
     if (GvIMPORTED(dstr) != GVf_IMPORTED
diff --git t/op/gv.t t/op/gv.t
index e695923..e368139 100644
--- t/op/gv.t
+++ t/op/gv.t
@@ -12,7 +12,7 @@ BEGIN {
 
 use warnings;
 
-plan( tests => 273 );
+plan( tests => 275 );
 
 # type coercion on assignment
 $foo = 'foo';
@@ -1132,6 +1132,11 @@ pass "No crash due to CvGV pointing to glob copy in the stash";
     is $z, 3, 'list assignment after aliasing [perl #89646]';
 }
 
+eval { $x=*0; *x=$x };
+ok $x, 'no crash glob assign with cross-references w/o accessing the GP [perl #125840]';
+eval { *x=$x=*a;$a=$x };
+ok $x, 'no crash glob assign with cross-references accessing the new GP [perl #125840]';
+
 
 __END__
 Perl
-- 
2.5.0

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2015

From @rurban

On Tue Aug 25 04​:07​:27 2015, rurban wrote​:

On Wed Aug 19 12​:10​:00 2015, hv wrote​:
Furthermore, the stack *IS* refcounted, even empty PADTMP entries.
Do you mean you want to refcount PL_curpad? There's no problem with
curpad here.

I see now what you mean that the stack isn't refcounted.
Explained here​:
http​://www.nntp.perl.org/group/perl.perl5.porters/2001/11/msg46878.html

My patches fixes that, because this free happened one line above.
Unrelated refs on the stack are of course tricky to fix. Most XS modules
might need to be rewritten
--
Reini Urban

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2015

From [Unknown Contact. See original ticket]

On Tue Aug 25 04​:07​:27 2015, rurban wrote​:

On Wed Aug 19 12​:10​:00 2015, hv wrote​:
Furthermore, the stack *IS* refcounted, even empty PADTMP entries.
Do you mean you want to refcount PL_curpad? There's no problem with
curpad here.

I see now what you mean that the stack isn't refcounted.
Explained here​:
http​://www.nntp.perl.org/group/perl.perl5.porters/2001/11/msg46878.html

My patches fixes that, because this free happened one line above.
Unrelated refs on the stack are of course tricky to fix. Most XS modules
might need to be rewritten
--
Reini Urban

@p5pRT
Copy link
Author

p5pRT commented Aug 30, 2015

From @iabyn

On Tue, Aug 25, 2015 at 04​:26​:33AM -0700, Reini Urban via RT wrote​:

Attached patch fixes all these glob assigns where the lhs references the rhs.

But your patch doesn't really fix anything; it just sometimes just detects
the premature free after the event, and avoids crashing. It still leaves
the globs containing the wrong things. And there's no guarantee that the
SV in question would still be freed - it may have been reallocated by a
DESTRUCTOR called while freeing the GP.

This issue is similar to things like $r = $$r.

The attached patch seems to fix it.

--
The Enterprise is involved in a bizarre time-warp experience which is in
some way unconnected with the Late 20th Century.
  -- Things That Never Happen in "Star Trek" #14

@p5pRT
Copy link
Author

p5pRT commented Aug 30, 2015

From @iabyn

0001-RT-125840-stop-x-x-doing-bad-things.patch
From 3c62f09f418b63bd79a6cbd20aaec4d992a6cc64 Mon Sep 17 00:00:00 2001
From: David Mitchell <davem@iabyn.com>
Date: Sun, 30 Aug 2015 09:48:28 +0100
Subject: [PATCH] RT #125840 stop *x = $x doing bad things

If $x is a GV then *x's GP would be freed before $x's GP is assigned to
it. That would prematurely free $x, so protect it with a temporary ref
count bump.
---
 sv.c      |  6 ++++++
 t/op/gv.t | 20 +++++++++++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/sv.c b/sv.c
index cd1bbf5..a10059d 100644
--- a/sv.c
+++ b/sv.c
@@ -3953,9 +3953,15 @@ S_glob_assign_glob(pTHX_ SV *const dstr, SV *const sstr, const int dtype)
         SvREFCNT_inc_simple_void_NN(sv_2mortal(dstr));
     }
 
+    /* freeing dstr's GP might free sstr (e.g. *x = $x),
+     * so temporarily protect it */
+    ENTER;
+    SAVEFREESV(SvREFCNT_inc_simple_NN(sstr));
     gp_free(MUTABLE_GV(dstr));
     GvINTRO_off(dstr);		/* one-shot flag */
     GvGP_set(dstr, gp_ref(GvGP(sstr)));
+    LEAVE;
+
     if (SvTAINTED(sstr))
 	SvTAINT(dstr);
     if (GvIMPORTED(dstr) != GVf_IMPORTED
diff --git a/t/op/gv.t b/t/op/gv.t
index e695923..2c9cc64 100644
--- a/t/op/gv.t
+++ b/t/op/gv.t
@@ -12,7 +12,7 @@ BEGIN {
 
 use warnings;
 
-plan( tests => 273 );
+plan(tests => 276 );
 
 # type coercion on assignment
 $foo = 'foo';
@@ -1132,6 +1132,24 @@ pass "No crash due to CvGV pointing to glob copy in the stash";
     is $z, 3, 'list assignment after aliasing [perl #89646]';
 }
 
+# RT #125840: make sure *x = $x doesn't do bad things by freeing $x before
+# it's assigned.
+
+{
+    $a_125840 = 1;
+    $b_125840 = 2;
+    $a_125840 = *b_125840;
+    *a_125840 = $a_125840;
+    is($a_125840, 2, 'RT #125840: *a = $a');
+
+    $c_125840 = 1;
+    $d_125840 = 2;
+    *d_125840 = $d_125840 = *c_125840;
+    is($d_125840, 1, 'RT #125840: *d=$d=*c');
+    $c_125840 = $d_125840;
+    is($c_125840, 1, 'RT #125840: $c=$d');
+}
+
 
 __END__
 Perl
-- 
1.9.3

@p5pRT
Copy link
Author

p5pRT commented Aug 30, 2015

From @iabyn

On Sun, Aug 30, 2015 at 02​:03​:06AM -0700, Dave Mitchell via RT wrote​:

On Tue, Aug 25, 2015 at 04​:26​:33AM -0700, Reini Urban via RT wrote​:

Attached patch fixes all these glob assigns where the lhs references the rhs.

But your patch doesn't really fix anything; it just sometimes just detects
the premature free after the event, and avoids crashing. It still leaves
the globs containing the wrong things. And there's no guarantee that the
SV in question would still be freed - it may have been reallocated by a
DESTRUCTOR called while freeing the GP.

This issue is similar to things like $r = $$r.

The attached patch seems to fix it.

Now pushed as

3c62f09

--
I don't want to achieve immortality through my work...
I want to achieve it through not dying.
  -- Woody Allen

@p5pRT
Copy link
Author

p5pRT commented Sep 1, 2015

@iabyn - Status changed from 'open' to 'pending release'

@p5pRT
Copy link
Author

p5pRT commented Sep 1, 2015

From @bulk88

On Sun Aug 30 02​:03​:05 2015, davem wrote​:

The attached patch seems to fix it.


Inline Patch
diff --git a/sv.c b/sv.c
index cd1bbf5..a10059d 100644
--- a/sv.c
+++ b/sv.c
@@ -3953,9 +3953,15 @@ S_glob_assign_glob(pTHX_ SV *const dstr, SV *const sstr, const int dtype)
         SvREFCNT_inc_simple_void_NN(sv_2mortal(dstr));
     }
 
+    /* freeing dstr's GP might free sstr (e.g. *x = $x),
+     * so temporarily protect it */
+    ENTER;
+    SAVEFREESV(SvREFCNT_inc_simple_NN(sstr));
     gp_free(MUTABLE_GV(dstr));
     GvINTRO_off(dstr);		/* one-shot flag */
     GvGP_set(dstr, gp_ref(GvGP(sstr)));
+    LEAVE;
+
     if (SvTAINTED(sstr))
 	SvTAINT(dstr);
     if (GvIMPORTED(dstr) != GVf_IMPORTED
----------------------------------------------------------------

This doesn't look efficient. Why not use SvREFCNT_inc_* and then mortal it vs open a new save stack scope? Why not skip the gp_free and/or gp_ref if GvGP(dstr) == GvGP(sstr) (think about the "steal" concept for refcnt==1+mortal scalars)?

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Sep 2, 2015

From @iabyn

On Tue, Sep 01, 2015 at 03​:02​:13PM -0700, bulk88 via RT wrote​:

On Sun Aug 30 02​:03​:05 2015, davem wrote​:

The attached patch seems to fix it.

----------------------------------------------------------------
diff --git a/sv.c b/sv.c
index cd1bbf5..a10059d 100644
--- a/sv.c
+++ b/sv.c
@​@​ -3953,9 +3953,15 @​@​ S_glob_assign_glob(pTHX_ SV *const dstr, SV *const sstr, const int dtype)
SvREFCNT_inc_simple_void_NN(sv_2mortal(dstr));
}

+ /* freeing dstr's GP might free sstr (e.g. *x = $x),
+ * so temporarily protect it */
+ ENTER;
+ SAVEFREESV(SvREFCNT_inc_simple_NN(sstr));
gp_free(MUTABLE_GV(dstr));
GvINTRO_off(dstr); /* one-shot flag */
GvGP_set(dstr, gp_ref(GvGP(sstr)));
+ LEAVE;
+
if (SvTAINTED(sstr))
SvTAINT(dstr);
if (GvIMPORTED(dstr) != GVf_IMPORTED
----------------------------------------------------------------

This doesn't look efficient.

It doesn't need to be. glob-to-glob assignments aren't exactly very
common, especially not in tight loops.

Why not use SvREFCNT_inc_* and then mortal it vs open a new save stack
scope?

Because mortalising it will delay the freeing of the RHS until some future
unspecified time depending on the nature of the caller, and is thus a
change in behaviour, to be avoided if possible.

Why not skip the gp_free and/or gp_ref
if GvGP(dstr) == GvGP(sstr) (think about the "steal" concept for
refcnt==1+mortal scalars)?

Well, that's a different use case from what's being addressed in this
ticket; but for that rare case (e.g. the second assignment in
*x = *y; *x = *y), then the existing code handles that just fine.
The GP will have a gp_refcnt of at least 2, so gp_free() just reduces the
refcount to 1, then GvGP_set(dstr, gp_ref(GvGP(sstr))) bumps it back up to
2, and the GP body remains untouched.

--
Please note that ash-trays are provided for the use of smokers,
whereas the floor is provided for the use of all patrons.
  -- Bill Royston

@p5pRT
Copy link
Author

p5pRT commented May 13, 2016

From @khwilliamson

Thank you for submitting this report. You have helped make Perl better.
 
With the release of Perl 5.24.0 on May 9, 2016, this and 149 other issues have been resolved.

Perl 5.24.0 may be downloaded via https://metacpan.org/release/RJBS/perl-5.24.0

@p5pRT p5pRT closed this as completed May 13, 2016
@p5pRT
Copy link
Author

p5pRT commented May 13, 2016

@khwilliamson - Status changed from 'pending release' to 'resolved'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant