This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
panic after cow-to-stash assignment
authorFather Chrysostomos <sprout@cpan.org>
Tue, 29 Nov 2011 02:31:55 +0000 (18:31 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Tue, 29 Nov 2011 04:41:03 +0000 (20:41 -0800)
This type of thing isn’t officially supported, but perl shouldn’t be
freeing unallocated memory (the 9th octet of a freed HEK) as a result:

    $::{whatever} = __PACKAGE__;
    *{"whatever"};

A string stored in the symbol table like that is actually a subroutine
stub.  ‘sub foo($)’ is stored as '$' in the "foo" slot to save space.

gv_init_pvn (formerly known as gv_init) checks SvPOK first thing,
assuming, if it is set, that it can reuse SvPVX as the CV’s prototype,
without reallocating or copying it.  That works most of the time.

For COW strings (such as those returned by __PACKAGE__), SvPVX points
to the hek_key field (the 9th octet) of a shared HEK.  When the CV is
freed, it ends up trying to do Safefree(that_hek + 8) effectively,
which is bad.

gv.c
t/op/gv.t

diff --git a/gv.c b/gv.c
index ad46000..6b78a8c 100644 (file)
--- a/gv.c
+++ b/gv.c
@@ -316,7 +316,9 @@ Perl_gv_init_pvn(pTHX_ GV *gv, HV *stash, const char *name, STRLEN len, U32 flag
     dVAR;
     const U32 old_type = SvTYPE(gv);
     const bool doproto = old_type > SVt_NULL;
-    char * const proto = (doproto && SvPOK(gv)) ? SvPVX(gv) : NULL;
+    char * const proto = (doproto && SvPOK(gv))
+       ? (SvIsCOW(gv) && (sv_force_normal((SV *)gv), 0), SvPVX(gv))
+       : NULL;
     const STRLEN protolen = proto ? SvCUR(gv) : 0;
     const U32 proto_utf8  = proto ? SvUTF8(gv) : 0;
     SV *const has_constant = doproto && SvROK(gv) ? SvRV(gv) : NULL;
index 0cb0d39..cbbb26b 100644 (file)
--- a/t/op/gv.t
+++ b/t/op/gv.t
@@ -930,6 +930,16 @@ package lrcg {
     'constants w/nulls in their names point 2 the right GVs when promoted';
 }
 
+# Look away, please.
+# This violates perl's internal structures by fiddling with stashes in a
+# way that should never happen, but perl should not start trying to free
+# unallocated memory as a result.  There is no ok() or is() because the
+# panic that used to occur only occurred during global destruction, and
+# only with PERL_DESTRUCT_LEVEL=2.  (The panic itself was sufficient for
+# the harness to consider this test script to have failed.)
+$::{aoeuaoeuaoeaoeu} = __PACKAGE__; # cow
+() = *{"aoeuaoeuaoeaoeu"};
+
 __END__
 Perl
 Rules