Blob Blame History Raw
From 42e9b60980bb8e29e76629e14c6aa945194c0647 Mon Sep 17 00:00:00 2001
From: Hugo van der Sanden <hv@crypt.org>
Date: Wed, 5 Oct 2016 02:20:26 +0100
Subject: [PATCH] [perl #129061] CURLYX nodes can be studied more than once
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

study_chunk() for CURLYX is used to set flags on the linked WHILEM
node to say it is the whilem_c'th of whilem_seen. However it assumes
each CURLYX can be studied only once, which is not the case - there
are various cases such as GOSUB which call study_chunk() recursively
on already-visited parts of the program.

Storing the wrong index can cause the super-linear cache handling in
regmatch() to read/write the byte after the end of poscache.

Also reported in [perl #129281].

Signed-off-by: Petr Písař <ppisar@redhat.com>
---
 regcomp.c  | 12 +++++++++---
 t/re/pat.t |  1 -
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/regcomp.c b/regcomp.c
index 850a6c1..48c8d8d 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -5218,15 +5218,21 @@ S_study_chunk(pTHX_ RExC_state_t *pRExC_state, regnode **scanp,
 			    However, this time it's not a subexpression
 			    we care about, but the expression itself. */
 			 && (maxcount == REG_INFTY)
-			 && data && ++data->whilem_c < 16) {
+			 && data) {
 		    /* This stays as CURLYX, we can put the count/of pair. */
 		    /* Find WHILEM (as in regexec.c) */
 		    regnode *nxt = oscan + NEXT_OFF(oscan);
 
 		    if (OP(PREVOPER(nxt)) == NOTHING) /* LONGJMP */
 			nxt += ARG(nxt);
-		    PREVOPER(nxt)->flags = (U8)(data->whilem_c
-			| (RExC_whilem_seen << 4)); /* On WHILEM */
+                    nxt = PREVOPER(nxt);
+                    if (nxt->flags & 0xf) {
+                        /* we've already set whilem count on this node */
+                    } else if (++data->whilem_c < 16) {
+                        assert(data->whilem_c <= RExC_whilem_seen);
+                        nxt->flags = (U8)(data->whilem_c
+                            | (RExC_whilem_seen << 4)); /* On WHILEM */
+                    }
 		}
 		if (data && fl & (SF_HAS_PAR|SF_IN_PAR))
 		    pars++;
diff --git a/t/re/pat.t b/t/re/pat.t
index ecd3af1..16bfc8e 100644
--- a/t/re/pat.t
+++ b/t/re/pat.t
@@ -1909,7 +1909,6 @@ EOP
     }
     {
         # [perl #129281] buffer write overflow, detected by ASAN, valgrind
-        local $::TODO = "whilem_c  bumped too much";
         fresh_perl_is('/0(?0)|^*0(?0)|^*(^*())0|/', '', {}, "don't bump whilem_c too much");
     }
 } # End of sub run_tests
-- 
2.7.4