diff options
author | Father Chrysostomos <sprout@cpan.org> | 2013-11-14 05:18:00 -0800 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2013-11-14 05:48:31 -0800 |
commit | bc81b34d0e30a0939f9a765397a98e4d1fdfa3e1 (patch) | |
tree | d97a7dea72f6c648f3ab1639b7321a9d7aeb4c5d /gv.c | |
parent | 300feaf1aad88e7e7a03a245926a5c63900f22d3 (diff) | |
download | perl-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.c | 7 |
1 files changed, 6 insertions, 1 deletions
@@ -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); } |