summaryrefslogtreecommitdiff
path: root/gv.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2013-11-14 05:18:00 -0800
committerFather Chrysostomos <sprout@cpan.org>2013-11-14 05:48:31 -0800
commitbc81b34d0e30a0939f9a765397a98e4d1fdfa3e1 (patch)
treed97a7dea72f6c648f3ab1639b7321a9d7aeb4c5d /gv.c
parent300feaf1aad88e7e7a03a245926a5c63900f22d3 (diff)
downloadperl-bc81b34d0e30a0939f9a765397a98e4d1fdfa3e1.tar.gz
Fix bad read in gp_free introduced by 4571f4a72
4571f4a72 attempted to fix this: $ ./perl -Ilib -e 'sub foo{} bless \&foo; DESTROY { undef *foo } undef *foo' Attempt to free unreferenced glob pointers, Perl interpreter: 0x7fd6a3803200 at -e line 1. by lowering the refcount of the gp only after freeing the contents. But what was missing was a check at the end to make sure the refcount was not already 0. In fact, that can’t work either, as, if the refcount has gone down to 0 while freeing the contents of the gp, we will be reading freed memory. In an attempt to implement what my half-baked idea was meant to do, I thought of incrementing the reference count before freeing the entries, and then checking it afterwards, but that would cause an ‘undef *foo’ during that to null the gp slot of the gv and replace it with a new one. In the mean time, gp_free is only freeing the old gp and the new gp will leak when it sets GvGP to 0. I thought of detaching the gp from the gv completely before freeing its entries, but most code doesn’t expect to see typeglobs with no gp. GvSV will crash. I nearly concluded that the only approach I could use was to revert 4571f4a72 and simply extirpate that warning. As far as I can tell, it only comes up with recursive gp_free calls. However, in those cases, simply returning will cause a memory leak, as pp_undef will give the gv a new gp, and the outer gp_free call, when it finishes, will set the gv’s gp slot to null, overwriting what pp_undef put there (a vari- ation of the leak above). So, the final solution is for gp_free to re-read the GvGP value each time it loops, freeing entries. The reference count will continue to be decremented at the end of the function, as in 4571f4a72. That will prevent any bad reads. If pp_undef has reallocated the gp in the mean time, we will simply start using the new one. We need an extra refer- ence count check at the end, in case a destructor does glob assignment and causes the gp to be shared. Ideally this should fix the recent build errors. (See <20131113045538.GA18816@mars.tony.develop-help.com>.) I tried it under valgrind on dromedary both before and after the patch, and the bad reads went away.
Diffstat (limited to 'gv.c')
-rw-r--r--gv.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/gv.c b/gv.c
index d29b1ef3cd..ebb42826c2 100644
--- a/gv.c
+++ b/gv.c
@@ -2352,6 +2352,7 @@ Perl_gp_free(pTHX_ GV *gv)
return;
}
if (gp->gp_refcnt > 1) {
+ borrowed:
if (gp->gp_egv == gv)
gp->gp_egv = 0;
gp->gp_refcnt--;
@@ -2396,6 +2397,9 @@ Perl_gp_free(pTHX_ GV *gv)
SvREFCNT_dec(cv);
SvREFCNT_dec(form);
+ /* Possibly reallocated by a destructor */
+ gp = GvGP(gv);
+
if (!gp->gp_file_hek
&& !gp->gp_sv
&& !gp->gp_av
@@ -2412,7 +2416,8 @@ Perl_gp_free(pTHX_ GV *gv)
}
}
- gp->gp_refcnt--;
+ /* Possibly incremented by a destructor doing glob assignment */
+ if (gp->gp_refcnt > 1) goto borrowed;
Safefree(gp);
GvGP_set(gv, NULL);
}