summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2011-08-18 08:50:02 -0700
committerFlorian Ragwitz <rafl@debian.org>2011-08-26 15:29:37 +0200
commitba4a1c05e90a8c6f86c1acb7c27b73650663e393 (patch)
treeb7bea161382902f6c502b664824e8fc5f7bd96a4
parente26bac9e7024fd691ef22af9c583f61139cc4ad4 (diff)
downloadperl-ba4a1c05e90a8c6f86c1acb7c27b73650663e393.tar.gz
[perl #97020] Carp (actually caller) leaking memory
Commit eff7e72c3 (Detect incomplete caller overrides in Carp) used this little trick for detecting a @DB::args that an overridden caller() failed to set: + @args = \$i; # A sentinal, which no-one else has the address of But there is a bug in caller(). The first time caller tries to write to @DB::args, it calls Perl_init_dbargs first. That function checks whether @DB::args is AvREAL, in case someone has assigned to it, and takes appropriate measures. But caller doesn’t bother calling Perl_init_dbargs more than once. So manually-assigned items in @DB::args would leak, starting with the *second* call to caller. Commit eff7e72c3 triggered that bug, resulting in a regression in Carp, in that it started leaking. eff7e72c3 was backported to 5.12.2 with commit 97705941a4, so in both 5.12 and 5.14 Carp is affected. This bug (the caller bug, not Carp’s triggering thereof) also affects any caller overrides that set @DB::args themselves, if there are alternate calls to the overridden caller and CORE::caller. This commit fixes that by changing the if (!PL_dbargs) condition in pp_caller to if (!PL_dbargs || AvREAL(PL_dbargs)). I.e., if @args is either uninitialised or AvREAL then call Perl_init_dbargs. Perl_init_dbargs also has a bug in it, that this fixes: The array not only needs AvREAL turned off, but also AvREIFY turned on, so that assignments to it that occur after its initialisation turn AvREAL back on again. (In fact, Larry Wall added a comment suggesting this back in perl 5.000.)
-rw-r--r--av.h2
-rw-r--r--perl.c2
-rw-r--r--pp_ctl.c2
-rw-r--r--t/op/caller.t13
4 files changed, 15 insertions, 4 deletions
diff --git a/av.h b/av.h
index 5231c4d4ce..de54fa9282 100644
--- a/av.h
+++ b/av.h
@@ -28,7 +28,7 @@ struct xpvav {
* real if the array needs to be modified in some way. Functions that
* modify fake AVs check both flags to call av_reify() as appropriate.
*
- * Note that the Perl stack and @DB::args have neither flag set. (Thus,
+ * Note that the Perl stack has neither flag set. (Thus,
* items that go on the stack are never refcounted.)
*
* These internal details are subject to change any time. AV
diff --git a/perl.c b/perl.c
index 8e918c29e9..f756e02dfd 100644
--- a/perl.c
+++ b/perl.c
@@ -3841,7 +3841,7 @@ Perl_init_dbargs(pTHX)
"leak" until global destruction. */
av_clear(args);
}
- AvREAL_off(PL_dbargs); /* XXX should be REIFY (see av.h) */
+ AvREIFY_only(PL_dbargs);
}
void
diff --git a/pp_ctl.c b/pp_ctl.c
index 9a8d96f820..60bc30d8b9 100644
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -1959,7 +1959,7 @@ PP(pp_caller)
AV * const ary = cx->blk_sub.argarray;
const int off = AvARRAY(ary) - AvALLOC(ary);
- if (!PL_dbargs)
+ if (!PL_dbargs || AvREAL(PL_dbargs))
Perl_init_dbargs(aTHX);
if (AvMAX(PL_dbargs) < AvFILLp(ary) + off)
diff --git a/t/op/caller.t b/t/op/caller.t
index a92b3eab21..d77088e611 100644
--- a/t/op/caller.t
+++ b/t/op/caller.t
@@ -5,7 +5,7 @@ BEGIN {
chdir 't' if -d 't';
@INC = '../lib';
require './test.pl';
- plan( tests => 81 );
+ plan( tests => 82 );
}
my @c;
@@ -214,6 +214,17 @@ EOP
}
}
+# This also used to leak [perl #97010]:
+{
+ my $gone;
+ sub fwib::DESTROY { ++$gone }
+ package DB;
+ sub { () = caller(0) }->(); # initialise PL_dbargs
+ @args = bless[],'fwib';
+ sub { () = caller(0) }->(); # clobber @args without initialisation
+ ::is $gone, 1, 'caller does not leak @DB::args elems when AvREAL';
+}
+
$::testing_caller = 1;
do './op/caller.pl' or die $@;