199 lines
8.4 KiB
Diff
199 lines
8.4 KiB
Diff
From f3704e62341b10824f503aa0c8029670d101a434 Mon Sep 17 00:00:00 2001
|
|
From: David Mitchell <davem@iabyn.com>
|
|
Date: Sat, 11 Feb 2017 11:53:41 +0000
|
|
Subject: [PATCH] fix pad/scope issue in re_evals
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Ported to 5.24.1:
|
|
commit 4b9c7caeaecf4e9df0be3a2e296644f763f775d6
|
|
Author: David Mitchell <davem@iabyn.com>
|
|
Date: Sat Feb 11 11:53:41 2017 +0000
|
|
|
|
fix pad/scope issue in re_evals
|
|
|
|
RT #129881 heap-buffer-overflow Perl_pad_sv
|
|
|
|
In some circumstances involving a pattern which has embedded code blocks
|
|
from more than one source, e.g.
|
|
|
|
my $r = qr{(?{1;}){2}X};
|
|
"" =~ /$r|(?{1;})/;
|
|
|
|
the wrong PL_comppad could be active while doing a LEAVE_SCOPE() or on
|
|
exit from the pattern.
|
|
|
|
This was mainly due to the big context stack changes in 5.24.0 - in
|
|
particular, since POP_MULTICALL() now does CX_LEAVE_SCOPE(cx) *before*
|
|
restoring PL_comppad, the (correct) unwinding of any SAVECOMPPAD's was
|
|
being followed by C<PL_comppad = cx->blk_sub.prevcomppad>, which wasn't
|
|
necessarily a sensible value.
|
|
|
|
To fix this, record the value of PL_savestack_ix at entry to S_regmatch(),
|
|
and set the cx->blk_oldsaveix of the MULTICALL to this value when pushed.
|
|
On exit from S_regmatch, we either POP_MULTICALL which will do a
|
|
LEAVE_SCOPE(cx->blk_oldsaveix), or in the absense of any EVAL, do the
|
|
explicit but equivalent LEAVE_SCOPE(orig_savestack_ix).
|
|
|
|
Note that this is a change in behaviour to S_regmatch() - formerly it
|
|
wouldn't necessarily clear the savestack completely back the point of
|
|
entry - that would get left to do by its caller, S_regtry(), or indirectly
|
|
by Perl_regexec_flags(). This shouldn't make any practical difference, but
|
|
is tidier and less likely to introduce bugs later.
|
|
|
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
|
---
|
|
regexec.c | 69 +++++++++++++++++++++++++++++++++++++++++++-----------
|
|
t/re/pat_re_eval.t | 20 +++++++++++++++-
|
|
2 files changed, 74 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/regexec.c b/regexec.c
|
|
index a7bc0c3..5656cdd 100644
|
|
--- a/regexec.c
|
|
+++ b/regexec.c
|
|
@@ -5233,6 +5233,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
|
|
_char_class_number classnum;
|
|
bool is_utf8_pat = reginfo->is_utf8_pat;
|
|
bool match = FALSE;
|
|
+ I32 orig_savestack_ix = PL_savestack_ix;
|
|
|
|
/* Solaris Studio 12.3 messes up fetching PL_charclass['\n'] */
|
|
#if (defined(__SUNPRO_C) && (__SUNPRO_C == 0x5120) && defined(__x86_64) && defined(USE_64_BIT_ALL))
|
|
@@ -6646,30 +6647,67 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
|
|
nop = (OP*)rexi->data->data[n];
|
|
}
|
|
|
|
- /* normally if we're about to execute code from the same
|
|
- * CV that we used previously, we just use the existing
|
|
- * CX stack entry. However, its possible that in the
|
|
- * meantime we may have backtracked, popped from the save
|
|
- * stack, and undone the SAVECOMPPAD(s) associated with
|
|
- * PUSH_MULTICALL; in which case PL_comppad no longer
|
|
- * points to newcv's pad. */
|
|
+ /* Some notes about MULTICALL and the context and save stacks.
|
|
+ *
|
|
+ * In something like
|
|
+ * /...(?{ my $x)}...(?{ my $z)}...(?{ my $z)}.../
|
|
+ * since codeblocks don't introduce a new scope (so that
|
|
+ * local() etc accumulate), at the end of a successful
|
|
+ * match there will be a SAVEt_CLEARSV on the savestack
|
|
+ * for each of $x, $y, $z. If the three code blocks above
|
|
+ * happen to have come from different CVs (e.g. via
|
|
+ * embedded qr//s), then we must ensure that during any
|
|
+ * savestack unwinding, PL_comppad always points to the
|
|
+ * right pad at each moment. We achieve this by
|
|
+ * interleaving SAVEt_COMPPAD's on the savestack whenever
|
|
+ * there is a change of pad.
|
|
+ * In theory whenever we call a code block, we should
|
|
+ * push a CXt_SUB context, then pop it on return from
|
|
+ * that code block. This causes a bit of an issue in that
|
|
+ * normally popping a context also clears the savestack
|
|
+ * back to cx->blk_oldsaveix, but here we specifically
|
|
+ * don't want to clear the save stack on exit from the
|
|
+ * code block.
|
|
+ * Also for efficiency we don't want to keep pushing and
|
|
+ * popping the single SUB context as we backtrack etc.
|
|
+ * So instead, we push a single context the first time
|
|
+ * we need, it, then hang onto it until the end of this
|
|
+ * function. Whenever we encounter a new code block, we
|
|
+ * update the CV etc if that's changed. During the times
|
|
+ * in this function where we're not executing a code
|
|
+ * block, having the SUB context still there is a bit
|
|
+ * naughty - but we hope that no-one notices.
|
|
+ * When the SUB context is initially pushed, we fake up
|
|
+ * cx->blk_oldsaveix to be as if we'd pushed this context
|
|
+ * on first entry to S_regmatch rather than at some random
|
|
+ * point during the regexe execution. That way if we
|
|
+ * croak, popping the context stack will ensure that
|
|
+ * *everything* SAVEd by this function is undone and then
|
|
+ * the context popped, rather than e.g., popping the
|
|
+ * context (and restoring the original PL_comppad) then
|
|
+ * popping more of the savestack and restoiring a bad
|
|
+ * PL_comppad.
|
|
+ */
|
|
+
|
|
+ /* If this is the first EVAL, push a MULTICALL. On
|
|
+ * subsequent calls, if we're executing a different CV, or
|
|
+ * if PL_comppad has got messed up from backtracking
|
|
+ * through SAVECOMPPADs, then refresh the context.
|
|
+ */
|
|
if (newcv != last_pushed_cv || PL_comppad != last_pad)
|
|
{
|
|
U8 flags = (CXp_SUB_RE |
|
|
((newcv == caller_cv) ? CXp_SUB_RE_FAKE : 0));
|
|
+ SAVECOMPPAD();
|
|
if (last_pushed_cv) {
|
|
- /* PUSH/POP_MULTICALL save and restore the
|
|
- * caller's PL_comppad; if we call multiple subs
|
|
- * using the same CX block, we have to save and
|
|
- * unwind the varying PL_comppad's ourselves,
|
|
- * especially restoring the right PL_comppad on
|
|
- * backtrack - so save it on the save stack */
|
|
- SAVECOMPPAD();
|
|
CHANGE_MULTICALL_FLAGS(newcv, flags);
|
|
}
|
|
else {
|
|
PUSH_MULTICALL_FLAGS(newcv, flags);
|
|
}
|
|
+ /* see notes above */
|
|
+ CX_CUR()->blk_oldsaveix = orig_savestack_ix;
|
|
+
|
|
last_pushed_cv = newcv;
|
|
}
|
|
else {
|
|
@@ -8456,9 +8494,12 @@ NULL
|
|
|
|
if (last_pushed_cv) {
|
|
dSP;
|
|
+ /* see "Some notes about MULTICALL" above */
|
|
POP_MULTICALL;
|
|
PERL_UNUSED_VAR(SP);
|
|
}
|
|
+ else
|
|
+ LEAVE_SCOPE(orig_savestack_ix);
|
|
|
|
assert(!result || locinput - reginfo->strbeg >= 0);
|
|
return result ? locinput - reginfo->strbeg : -1;
|
|
diff --git a/t/re/pat_re_eval.t b/t/re/pat_re_eval.t
|
|
index e59b059..1a0b228 100644
|
|
--- a/t/re/pat_re_eval.t
|
|
+++ b/t/re/pat_re_eval.t
|
|
@@ -22,7 +22,7 @@ BEGIN {
|
|
}
|
|
|
|
|
|
-plan tests => 527; # Update this when adding/deleting tests.
|
|
+plan tests => 530; # Update this when adding/deleting tests.
|
|
|
|
run_tests() unless caller;
|
|
|
|
@@ -1232,6 +1232,24 @@ sub run_tests {
|
|
'padtmp swiping does not affect "$a$b" =~ /(??{})/'
|
|
}
|
|
|
|
+ # RT #129881
|
|
+ # on exit from a pattern with multiple code blocks from different
|
|
+ # CVs, PL_comppad wasn't being restored correctly
|
|
+
|
|
+ sub {
|
|
+ # give first few pad slots known values
|
|
+ my ($x1, $x2, $x3, $x4, $x5) = 101..105;
|
|
+ # these vars are in a separate pad
|
|
+ my $r = qr/((?{my ($y1, $y2) = 201..202; 1;})A){2}X/;
|
|
+ # the first alt fails, causing a switch to this anon
|
|
+ # sub's pad
|
|
+ "AAA" =~ /$r|(?{my ($z1, $z2) = 301..302; 1;})A/;
|
|
+ is $x1, 101, "RT #129881: x1";
|
|
+ is $x2, 102, "RT #129881: x2";
|
|
+ is $x3, 103, "RT #129881: x3";
|
|
+ }->();
|
|
+
|
|
+
|
|
} # End of sub run_tests
|
|
|
|
1;
|
|
--
|
|
2.7.4
|
|
|