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