diff options
author | David Mitchell <davem@iabyn.com> | 2017-08-04 14:28:15 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2017-08-04 14:33:30 +0100 |
commit | 622c613e12cef84c93c5df207a321fe13d0f2a7f (patch) | |
tree | 6269bb97139666af24e2eec64fc489588ef7528d /sv.c | |
parent | 89042fa4090fc5634ff775e753c58b0827ad6af8 (diff) | |
download | perl-622c613e12cef84c93c5df207a321fe13d0f2a7f.tar.gz |
PVLV-as-REGEXP: avoid PVX double free
With v5.27.2-30-gdf6b4bd, I changed the way that PVLVs store a regexp
value (by making the xpv_len field point to a regexp struct). There was a
bug in this, which caused the PVX buffer to be double freed.
Several REGEXP SVs can share a PVX buffer. Only one of them will have a
non-zero xpv_len field, and that SV gets to free the buffer.
After the commit above, the non-zero xpv_len was triggering an extra free.
This was showing up in smokes as failures in re/recompile.t when invoked
with PERL_DESTRUCT_LEVEL=2 (which t/TEST does).
Diffstat (limited to 'sv.c')
-rw-r--r-- | sv.c | 12 |
1 files changed, 10 insertions, 2 deletions
@@ -6624,7 +6624,6 @@ Perl_sv_clear(pTHX_ SV *const orig_sv) goto freescalar; case SVt_REGEXP: /* FIXME for plugins */ - freeregexp: pregfree2((REGEXP*) sv); goto freescalar; case SVt_PVCV: @@ -6703,7 +6702,16 @@ Perl_sv_clear(pTHX_ SV *const orig_sv) } else if (LvTYPE(sv) != 't') /* unless tie: unrefcnted fake SV** */ SvREFCNT_dec(LvTARG(sv)); - if (isREGEXP(sv)) goto freeregexp; + if (isREGEXP(sv)) { + /* SvLEN points to a regex body. Free the body, then + * set SvLEN to whatever value was in the now-freed + * regex body. The PVX buffer is shared by multiple re's + * and only freed once, by the re whose len in non-null */ + STRLEN len = ReANY(sv)->xpv_len; + pregfree2((REGEXP*) sv); + SvLEN_set((sv), len); + goto freescalar; + } /* FALLTHROUGH */ case SVt_PVGV: if (isGV_with_GP(sv)) { |