[perl #91880] $_ refcounting problems in @INC filters
authorFather Chrysostomos <sprout@cpan.org>
Mon, 30 May 2011 15:55:40 +0000 (08:55 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Mon, 30 May 2011 15:55:40 +0000 (08:55 -0700)
In @INC filters (subs returned by subs in @INC), $_ is localised to a
variable to which the next line of source code is to be assigned. The
function in pp_ctl.c that calls it (S_run_user_filter) has a pointer
to that variable.

Up till now, it was not setting the refcount or localising
$_ properly.

‘undef *_’ inside the sub would destroy the only refcount it
had, leaving a freed sv for toke.c to parse (which would crash,
of course).

In some cases, S_run_user_filter has to created a new variable. In
those cases, it was setting $_ to a mortal variable with the TEMP
flag, but with a refcount of 1, which would result in ‘Attempt to free
unreferenced scalar’ warnings if the $_ were freed by the subroutine.

This commit changes S_run_user_filter to use SAVEGENERICSV, rather
than SAVE_DEFSV, to localise $_, since the former lowers the refcount
on scope exit, while the latter does not. So now I have also made it
increase the refcount after assigning to the now-properly-localised $_
(DEFSV). I also turned off the TEMP flag, to avoid weird side effects
(which were what led me to this bug to begin with).

pp_ctl.c
t/op/incfilter.t

index 0df8b5f..16386a8 100644 (file)
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -5232,6 +5232,7 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen)
        I'm going to use a mortal in case the upstream filter croaks.  */
     upstream = ((SvOK(buf_sv) && sv_len(buf_sv)) || SvGMAGICAL(buf_sv))
        ? sv_newmortal() : buf_sv;
+    SvTEMP_off(upstream);
     SvUPGRADE(upstream, SVt_PV);
        
     if (filter_has_file) {
@@ -5243,11 +5244,12 @@ S_run_user_filter(pTHX_ int idx, SV *buf_sv, int maxlen)
        int count;
 
        ENTER_with_name("call_filter_sub");
-       SAVE_DEFSV;
+       SAVEGENERICSV(GvSV(PL_defgv));
        SAVETMPS;
        EXTEND(SP, 2);
 
        DEFSV_set(upstream);
+       SvREFCNT_inc_simple_void_NN(upstream);
        PUSHMARK(SP);
        mPUSHi(0);
        if (filter_state) {
index 74675a2..9db4f7d 100644 (file)
@@ -13,7 +13,7 @@ use strict;
 use Config;
 use Filter::Util::Call;
 
-plan(tests => 143);
+plan(tests => 145);
 
 unshift @INC, sub {
     no warnings 'uninitialized';
@@ -227,3 +227,23 @@ for (0 .. 1) {
        \'like(__FILE__, qr/(?:GLOB|CODE)\(0x[0-9a-f]+\)/, "__FILE__ is valid");';
     do $fh or die;
 }
+
+# [perl #91880] $_ marked TEMP or having the wrong refcount inside a
+{ #             filter sub
+    local @INC; local $|;
+    unshift @INC, sub { sub { undef *_; --$| }};
+    do "dah";
+    pass '$_ has the right refcount inside a filter sub';
+
+    my $temps = 0;
+    @INC = sub { sub {
+       my $temp = \sub{$_}->();
+       $temps++ if $temp == \$_;
+       $_ = "a" unless $|;
+       return --$|
+    }};
+    local $^W;
+    do "dah";
+
+    is $temps, 0, '$_ is not marked TEMP';
+}