Blob Blame History Raw
From 53469ce0ff5dac0aadfda412c4fa243e341e4b30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
Date: Thu, 9 Apr 2015 18:35:04 +0200
Subject: [PATCH] Fix stack overflow instead of diagnostic for mutual recursion
 inside a lookbehind assertion.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Upstream commit porter to 8.36:

commit 256d94987eecd7eb87b37e1c981a4e753ed8ab7a
Author: ph10 <ph10@2f5784b3-3f2a-0410-8824-cb99058d5e15>
Date:   Wed Apr 1 15:43:53 2015 +0000

    Fix stack overflow instead of diagnostic for mutual recursion inside a
    lookbehind assertion.

    git-svn-id: svn://vcs.exim.org/pcre/code/trunk@1542 2f5784b3-3f2a-0410-8824-cb99058d5e15

Signed-off-by: Petr Písař <ppisar@redhat.com>
---
 pcre_compile.c       | 36 +++++++++++++++++++++++++-----------
 testdata/testinput2  |  2 ++
 testdata/testoutput2 |  3 +++
 3 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/pcre_compile.c b/pcre_compile.c
index 603e50a..ec7ecf4 100644
--- a/pcre_compile.c
+++ b/pcre_compile.c
@@ -866,6 +866,14 @@ static const pcre_uint8 opcode_possessify[] = {
 };
 
 
+/* Structure for mutual recursion detection. */
+
+typedef struct recurse_check {
+  struct recurse_check *prev;
+  const pcre_uchar *group;
+} recurse_check;
+
+
 
 /*************************************************
 *            Find an error text                  *
@@ -1704,6 +1712,7 @@ Arguments:
   utf      TRUE in UTF-8 / UTF-16 / UTF-32 mode
   atend    TRUE if called when the pattern is complete
   cd       the "compile data" structure
+  recurses    chain of recurse_check to catch mutual recursion
 
 Returns:   the fixed length,
              or -1 if there is no fixed length,
@@ -1713,10 +1722,11 @@ Returns:   the fixed length,
 */
 
 static int
-find_fixedlength(pcre_uchar *code, BOOL utf, BOOL atend, compile_data *cd)
+find_fixedlength(pcre_uchar *code, BOOL utf, BOOL atend, compile_data *cd,
+  recurse_check *recurses)
 {
 int length = -1;
-
+recurse_check this_recurse;
 register int branchlength = 0;
 register pcre_uchar *cc = code + 1 + LINK_SIZE;
 
@@ -1741,7 +1751,8 @@ for (;;)
     case OP_ONCE:
     case OP_ONCE_NC:
     case OP_COND:
-    d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cd);
+    d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cd,
+      recurses);
     if (d < 0) return d;
     branchlength += d;
     do cc += GET(cc, 1); while (*cc == OP_ALT);
@@ -1775,7 +1786,15 @@ for (;;)
     cs = ce = (pcre_uchar *)cd->start_code + GET(cc, 1);  /* Start subpattern */
     do ce += GET(ce, 1); while (*ce == OP_ALT);           /* End subpattern */
     if (cc > cs && cc < ce) return -1;                    /* Recursion */
-    d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cd);
+    else   /* Check for mutual recursion */
+      {
+      recurse_check *r = recurses;
+      for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
+      if (r != NULL) return -1;   /* Mutual recursion */
+      }
+    this_recurse.prev = recurses;
+    this_recurse.group = cs;
+    d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cd, &this_recurse);
     if (d < 0) return d;
     branchlength += d;
     cc += 1 + LINK_SIZE;
@@ -2334,11 +2353,6 @@ Arguments:
 Returns:      TRUE if what is matched could be empty
 */
 
-typedef struct recurse_check {
-  struct recurse_check *prev;
-  const pcre_uchar *group;
-} recurse_check;
-
 static BOOL
 could_be_empty_branch(const pcre_uchar *code, const pcre_uchar *endcode,
   BOOL utf, compile_data *cd, recurse_check *recurses)
@@ -8225,7 +8239,7 @@ for (;;)
       int fixed_length;
       *code = OP_END;
       fixed_length = find_fixedlength(last_branch,  (options & PCRE_UTF8) != 0,
-        FALSE, cd);
+        FALSE, cd, NULL);
       DPRINTF(("fixed length = %d\n", fixed_length));
       if (fixed_length == -3)
         {
@@ -9332,7 +9346,7 @@ if (cd->check_lookbehind)
       int end_op = *be;
       *be = OP_END;
       fixed_length = find_fixedlength(cc, (re->options & PCRE_UTF8) != 0, TRUE,
-        cd);
+        cd, NULL);
       *be = end_op;
       DPRINTF(("fixed length = %d\n", fixed_length));
       if (fixed_length < 0)
diff --git a/testdata/testinput2 b/testdata/testinput2
index c01a094..0fc535a 100644
--- a/testdata/testinput2
+++ b/testdata/testinput2
@@ -4086,4 +4086,6 @@ backtracking verbs. --/
 
 "((?2)+)((?1))"
 
+"(?<=((?2))((?1)))"
+
 /-- End of testinput2 --/
diff --git a/testdata/testoutput2 b/testdata/testoutput2
index 0118730..45662a9 100644
--- a/testdata/testoutput2
+++ b/testdata/testoutput2
@@ -14218,4 +14218,7 @@ No match
 
 "((?2)+)((?1))"
 
+"(?<=((?2))((?1)))"
+Failed: lookbehind assertion is not fixed length at offset 17
+
 /-- End of testinput2 --/
-- 
2.1.0