Make PL_DBgv and other db interp vars refcounted
authorFather Chrysostomos <sprout@cpan.org>
Sat, 26 Oct 2013 13:00:59 +0000 (06:00 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 26 Oct 2013 13:00:59 +0000 (06:00 -0700)
$ PERL5DB=1 perl -de '*DB::DB = sub {} if 0; sub DB::DB{}'
No DB::DB routine defined at -e line 1.

When the ‘if’ gets folded and *DB::DB is freed, it triggers
gv_try_downgrade, causing $DB::{DB} to be deleted and PL_DBgv to point
to a freed SV.  A higher refcount would prevent gv_try_downgrade from
touching it.

perl.c
sv.c
t/run/switchd.t

diff --git a/perl.c b/perl.c
index 1835062..c4726c4 100644 (file)
--- a/perl.c
+++ b/perl.c
@@ -959,9 +959,6 @@ perl_destruct(pTHXx)
     PL_stderrgv = NULL;
     PL_last_in_gv = NULL;
     PL_replgv = NULL;
-    PL_DBgv = NULL;
-    PL_DBline = NULL;
-    PL_DBsub = NULL;
     PL_DBsingle = NULL;
     PL_DBtrace = NULL;
     PL_DBsignal = NULL;
@@ -969,6 +966,13 @@ perl_destruct(pTHXx)
     PL_dbargs = NULL;
     PL_debstash = NULL;
 
+    SvREFCNT_dec(PL_DBgv);
+    SvREFCNT_dec(PL_DBline);
+    SvREFCNT_dec(PL_DBsub);
+    PL_DBgv = NULL;
+    PL_DBline = NULL;
+    PL_DBsub = NULL;
+
     SvREFCNT_dec(PL_argvout_stack);
     PL_argvout_stack = NULL;
 
@@ -4043,9 +4047,15 @@ Perl_init_debugger(pTHX)
     PL_curstash = (HV *)SvREFCNT_inc_simple(PL_debstash);
 
     Perl_init_dbargs(aTHX);
-    PL_DBgv = gv_fetchpvs("DB::DB", GV_ADDMULTI, SVt_PVGV);
-    PL_DBline = gv_fetchpvs("DB::dbline", GV_ADDMULTI, SVt_PVAV);
-    PL_DBsub = gv_HVadd(gv_fetchpvs("DB::sub", GV_ADDMULTI, SVt_PVHV));
+    PL_DBgv = MUTABLE_GV(
+       SvREFCNT_inc(gv_fetchpvs("DB::DB", GV_ADDMULTI, SVt_PVGV))
+    );
+    PL_DBline = MUTABLE_GV(
+       SvREFCNT_inc(gv_fetchpvs("DB::dbline", GV_ADDMULTI, SVt_PVAV))
+    );
+    PL_DBsub = MUTABLE_GV(SvREFCNT_inc(
+       gv_HVadd(gv_fetchpvs("DB::sub", GV_ADDMULTI, SVt_PVHV))
+    ));
     PL_DBsingle = GvSV((gv_fetchpvs("DB::single", GV_ADDMULTI, SVt_PV)));
     if (!SvIOK(PL_DBsingle))
        sv_setiv(PL_DBsingle, 0);
diff --git a/sv.c b/sv.c
index 99b1182..f174b18 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -13620,9 +13620,9 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_errgv           = gv_dup(proto_perl->Ierrgv, param);
 
     /* shortcuts to debugging objects */
-    PL_DBgv            = gv_dup(proto_perl->IDBgv, param);
-    PL_DBline          = gv_dup(proto_perl->IDBline, param);
-    PL_DBsub           = gv_dup(proto_perl->IDBsub, param);
+    PL_DBgv            = gv_dup_inc(proto_perl->IDBgv, param);
+    PL_DBline          = gv_dup_inc(proto_perl->IDBline, param);
+    PL_DBsub           = gv_dup_inc(proto_perl->IDBsub, param);
     PL_DBsingle                = sv_dup(proto_perl->IDBsingle, param);
     PL_DBtrace         = sv_dup(proto_perl->IDBtrace, param);
     PL_DBsignal                = sv_dup(proto_perl->IDBsignal, param);
index e66a17f..1b5141c 100644 (file)
@@ -9,7 +9,7 @@ BEGIN { require "./test.pl"; }
 
 # This test depends on t/lib/Devel/switchd*.pm.
 
-plan(tests => 14);
+plan(tests => 15);
 
 my $r;
 
@@ -147,6 +147,16 @@ like(
   qr/^No DB::DB routine defined/,
   "No crash when &DB::DB exists but isn't actually defined",
 );
+# or seen and defined later
+is(
+  runperl(
+    switches => [ '-Ilib', '-d:nodb' ], # nodb.pm contains *DB::DB...if 0
+    prog     => 'warn; sub DB::DB { print qq-ok\n-; exit }',
+    stderr   => 1,
+  ),
+  "ok\n",
+  "DB::DB works after '*DB::DB if 0'",
+);
 
 # [perl #115742] Recursive DB::DB clobbering its own pad
 like(