This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Stop eval "BEGIN{die}" from leaking
authorFather Chrysostomos <sprout@cpan.org>
Fri, 26 Nov 2010 14:25:36 +0000 (06:25 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Fri, 26 Nov 2010 14:25:53 +0000 (06:25 -0800)
commit78da7625590089213831ed5137e24598b0cd3cea
tree06375650abb3d173f2b1cf83cc2085d4b8307eaa
parentfd8c3383aea40326a20c5e582ecf50ee7d7bfcb9
Stop eval "BEGIN{die}" from leaking

This fixes the rest of [perl #78438].

eval "BEGIN{die}" creates a *{"_<(eval 1)"} glob regardless of $^P’s
setting in non-threaded builds as of change f9bddea (5.12.0).

Here are the results with various configurations:

version  threaded  eval text  $^P  Is *{"_<(eval 1)"} set?
-------  --------  ---------  ---  -----------------------
5.10.1   yes       BEGIN{}      0  no
5.10.1   yes       BEGIN{die}   0  no
5.10.1   yes       BEGIN{}    0xA  yes
5.10.1   yes       BEGIN{die} 0xA  no

5.10.1   no        BEGIN{}      0  no
5.10.1   no        BEGIN{die}   0  no
5.10.1   no        BEGIN{}    0xA  yes
5.10.1   no        BEGIN{die} 0xA  no

5.13.7   yes       BEGIN{}      0  no
5.13.7   yes       BEGIN{die}   0  no
5.13.7   yes       BEGIN{}    0xA  yes
5.13.7   yes       BEGIN{die} 0xA  yes

5.13.7   no        BEGIN{}      0  no
5.13.7   no        BEGIN{die}   0  yes
5.13.7   no        BEGIN{}    0xA  yes
5.13.7   no        BEGIN{die} 0xA  yes

Notice that, for non-threaded builds, BEGIN{die} goes from never sav-
ing the text to always saving it.

The commit in question is:

commit f9bddea7d2a0d824366014c8ee6ba57e7dedd8c3
Author: Nicholas Clark <nick@ccl4.org>
Date:   Tue Dec 2 20:43:58 2008 +0000

    Implement PERLDBf_SAVESRC_INVALID, which saves source lines for string
    evals that fail to compile.

    p4raw-id: //depot/perl@34985

It stops unconditionally using the scoping mechanism to delete
$::{"_<(eval $num)"} on compilation failure:

-    safestr = savepvn(tmpbuf, len);
-    SAVEDELETE(PL_defstash, safestr, len);

but instead does it explicitly in this block:

+    if (doeval(gimme, NULL, runcv, seq)) {
+ if (was != PL_breakable_sub_gen /* Some subs defined here. */
+     ? (PERLDB_LINE || PERLDB_SAVESRC)
+     :  PERLDB_SAVESRC_NOSUBS) {
+     /* Retain the filegv we created.  */
+ } else {
+     char *const safestr = savepvn(tmpbuf, len);
+     SAVEDELETE(PL_defstash, safestr, len);
+ }
+ return DOCATCH(PL_eval_start);
+    } else {
+ /* We have already left the scope set up earler thanks to the LEAVE
+    in doeval().  */
+ if (PERLDB_SAVESRC_INVALID) {
+     /* Retain the filegv we created.  */
+ } else {
+     (void)hv_delete(PL_defstash, tmpbuf, len, G_DISCARD);
+ }
+ return PL_op->op_next;
+    }

In the case of BEGIN{die}, that doeval() never returns, so the
clean-up code is not reached.

S_doeval never returns because call_list calls Perl_croak if it
catches a BEGIN error (appending the extra ‘BEGIN failed--compilation
aborted’, etc.). That takes execution all the way back to perl_run, so
it bypasses the clean-up code in pp_entereval.

What’s leaking is the GV created earlier in pp_entereval by this line:

    CopFILE_set(&PL_compiling, tmpbuf+2);

CopFILE_set simply stores a string under threads, but creates a GV
under non-threaded builds.

This commit solves the problem by scheduling a deletion *before* call-
ing doeval, if the source lines have not been saved.

This works because the usual code to handle it is only bypassed when
there is a BEGIN block (a subroutine), so PL_breakable_sub_gen will
have gone up. So we never need to delete the saved lines when that
code is bypassed.
pp_ctl.c
t/comp/retainedlines.t