diff options
author | Father Chrysostomos <sprout@cpan.org> | 2013-06-06 08:47:31 -0700 |
---|---|---|
committer | Ricardo Signes <rjbs@cpan.org> | 2013-07-31 22:35:19 -0400 |
commit | d74c14318fb246d7ee7c7be91c9e4f96bb149cc4 (patch) | |
tree | 82abcc606335ad1071ebf9919e711cba5a96a126 | |
parent | abdbe03f98120e5bab9d1baa2a6f1b98890a864f (diff) | |
download | perl-d74c14318fb246d7ee7c7be91c9e4f96bb149cc4.tar.gz |
Stop (?[]) operators from leaking
When a (?[]) extended charclass is compiled, the various operands are
stored as inversion lists in separate SVs and then combined together
into new inversion lists. The functions that take care of combining
inversion lists only ever free one operand, and sometimes free none.
Most of the operators in (?[]) were trusting the invlist functions to
free everything that was no longer needed, causing (?[]) compilation
to leak invlists.
(cherry picked from commit a84e671a269f736a404a62f21caacc8a431c2aca)
-rw-r--r-- | regcomp.c | 20 | ||||
-rw-r--r-- | t/op/svleak.t | 8 |
2 files changed, 21 insertions, 7 deletions
@@ -11919,6 +11919,7 @@ S_handle_regex_sets(pTHX_ RExC_state_t *pRExC_state, SV** return_invlist, I32 *f } else { SV* top = av_pop(stack); + SV *prev = NULL; char current_operator; if (IS_OPERAND(top)) { @@ -11948,7 +11949,8 @@ S_handle_regex_sets(pTHX_ RExC_state_t *pRExC_state, SV** return_invlist, I32 *f goto handle_operand; case '&': - _invlist_intersection(av_pop(stack), + prev = av_pop(stack); + _invlist_intersection(prev, current, ¤t); av_push(stack, current); @@ -11956,12 +11958,14 @@ S_handle_regex_sets(pTHX_ RExC_state_t *pRExC_state, SV** return_invlist, I32 *f case '|': case '+': - _invlist_union(av_pop(stack), current, ¤t); + prev = av_pop(stack); + _invlist_union(prev, current, ¤t); av_push(stack, current); break; case '-': - _invlist_subtract(av_pop(stack), current, ¤t); + prev = av_pop(stack);; + _invlist_subtract(prev, current, ¤t); av_push(stack, current); break; @@ -11971,9 +11975,12 @@ S_handle_regex_sets(pTHX_ RExC_state_t *pRExC_state, SV** return_invlist, I32 *f SV* u = NULL; SV* element; - element = av_pop(stack); - _invlist_union(element, current, &u); - _invlist_intersection(element, current, &i); + prev = av_pop(stack); + _invlist_union(prev, current, &u); + _invlist_intersection(prev, current, &i); + /* _invlist_subtract will overwrite current + without freeing what it already contains */ + element = current; _invlist_subtract(u, i, ¤t); av_push(stack, current); SvREFCNT_dec_NN(i); @@ -11986,6 +11993,7 @@ S_handle_regex_sets(pTHX_ RExC_state_t *pRExC_state, SV** return_invlist, I32 *f Perl_croak(aTHX_ "panic: Unexpected item on '(?[ ])' stack"); } SvREFCNT_dec_NN(top); + SvREFCNT_dec(prev); } } diff --git a/t/op/svleak.t b/t/op/svleak.t index 8140ded505..b97a5cbd4c 100644 --- a/t/op/svleak.t +++ b/t/op/svleak.t @@ -15,7 +15,7 @@ BEGIN { use Config; -plan tests => 115; +plan tests => 121; # run some code N times. If the number of SVs at the end of loop N is # greater than (N-1)*delta at the end of loop 1, we've got a leak @@ -241,6 +241,12 @@ eleak(2,0,'/[[:ascii:]]/'); eleak(2,0,'/[[.zog.]]/'); eleak(2,0,'/[.zog.]/'); eleak(2,0,'no warnings; /(?[])/'); +eleak(2,0,'no warnings; /(?[[a]+[b]])/'); +eleak(2,0,'no warnings; /(?[[a]-[b]])/'); +eleak(2,0,'no warnings; /(?[[a]&[b]])/'); +eleak(2,0,'no warnings; /(?[[a]|[b]])/'); +eleak(2,0,'no warnings; /(?[[a]^[b]])/'); +eleak(2,0,'no warnings; /(?[![a]])/'); # These can generate one ref count, but just once. eleak(4,1,'chr(0x100) =~ /[[:punct:]]/'); |