summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2013-06-06 08:47:31 -0700
committerRicardo Signes <rjbs@cpan.org>2013-07-31 22:35:19 -0400
commitd74c14318fb246d7ee7c7be91c9e4f96bb149cc4 (patch)
tree82abcc606335ad1071ebf9919e711cba5a96a126
parentabdbe03f98120e5bab9d1baa2a6f1b98890a864f (diff)
downloadperl-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.c20
-rw-r--r--t/op/svleak.t8
2 files changed, 21 insertions, 7 deletions
diff --git a/regcomp.c b/regcomp.c
index 9480f9e8df..ce6be94936 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -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,
&current);
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, &current);
+ prev = av_pop(stack);
+ _invlist_union(prev, current, &current);
av_push(stack, current);
break;
case '-':
- _invlist_subtract(av_pop(stack), current, &current);
+ prev = av_pop(stack);;
+ _invlist_subtract(prev, current, &current);
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, &current);
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:]]/');