+From b3f0b0dd971314df8f865e221aa1a88e75d6d1a6 Mon Sep 17 00:00:00 2001
+From: ph10 <ph10@2f5784b3-3f2a-0410-8824-cb99058d5e15>
+Date: Wed, 5 Aug 2015 15:38:32 +0000
+Subject: [PATCH] Fix buffer overflow for named references in (?| situations.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Ported for 8.37:
+
+commit 7af8e8717def179fd7b69e173abd347c1a3547cb
+Author: ph10 <ph10@2f5784b3-3f2a-0410-8824-cb99058d5e15>
+Date: Wed Aug 5 15:38:32 2015 +0000
+
+ Fix buffer overflow for named references in (?| situations.
+
+ git-svn-id: svn://vcs.exim.org/pcre/code/trunk@1585 2f5784b3-3f2a-0410-8824-cb99058d5e15
+
+Signed-off-by: Petr Písař <ppisar@redhat.com>
+---
+ pcre_compile.c | 74 ++++++++++++++++++++++++++++++----------------------
+ pcre_internal.h | 1 +
+ testdata/testinput2 | 2 ++
+ testdata/testoutput2 | 2 ++
+ 4 files changed, 48 insertions(+), 31 deletions(-)
+
+diff --git a/pcre_compile.c b/pcre_compile.c
+index f5d2384..5fe5c1d 100644
+--- a/pcre_compile.c
++++ b/pcre_compile.c
+@@ -6641,6 +6641,7 @@ for (;; ptr++)
+ /* ------------------------------------------------------------ */
+ case CHAR_VERTICAL_LINE: /* Reset capture count for each branch */
+ reset_bracount = TRUE;
++ cd->dupgroups = TRUE; /* Record (?| encountered */
+ /* Fall through */
+
+ /* ------------------------------------------------------------ */
+@@ -7151,7 +7152,8 @@ for (;; ptr++)
+ if (lengthptr != NULL)
+ {
+ named_group *ng;
+-
++ recno = 0;
++
+ if (namelen == 0)
+ {
+ *errorcodeptr = ERR62;
+@@ -7168,32 +7170,6 @@ for (;; ptr++)
+ goto FAILED;
+ }
+
+- /* The name table does not exist in the first pass; instead we must
+- scan the list of names encountered so far in order to get the
+- number. If the name is not found, set the value to 0 for a forward
+- reference. */
+-
+- recno = 0;
+- ng = cd->named_groups;
+- for (i = 0; i < cd->names_found; i++, ng++)
+- {
+- if (namelen == ng->length &&
+- STRNCMP_UC_UC(name, ng->name, namelen) == 0)
+- {
+- open_capitem *oc;
+- recno = ng->number;
+- if (is_recurse) break;
+- for (oc = cd->open_caps; oc != NULL; oc = oc->next)
+- {
+- if (oc->number == recno)
+- {
+- oc->flag = TRUE;
+- break;
+- }
+- }
+- }
+- }
+-
+ /* Count named back references. */
+
+ if (!is_recurse) cd->namedrefcount++;
+@@ -7215,7 +7191,44 @@ for (;; ptr++)
+ issue is fixed "properly" in PCRE2. As PCRE1 is now in maintenance
+ only mode, we finesse the bug by allowing more memory always. */
+
+- /* if (recno == 0) */ *lengthptr += 2 + 2*LINK_SIZE;
++ *lengthptr += 2 + 2*LINK_SIZE;
++
++ /* It is even worse than that. The current reference may be to an
++ existing named group with a different number (so apparently not
++ recursive) but which later on is also attached to a group with the
++ current number. This can only happen if $(| has been previous
++ encountered. In that case, we allow yet more memory, just in case.
++ (Again, this is fixed "properly" in PCRE2. */
++
++ if (cd->dupgroups) *lengthptr += 2 + 2*LINK_SIZE;
++
++ /* Otherwise, check for recursion here. The name table does not exist
++ in the first pass; instead we must scan the list of names encountered
++ so far in order to get the number. If the name is not found, leave
++ the value of recno as 0 for a forward reference. */
++
++ else
++ {
++ ng = cd->named_groups;
++ for (i = 0; i < cd->names_found; i++, ng++)
++ {
++ if (namelen == ng->length &&
++ STRNCMP_UC_UC(name, ng->name, namelen) == 0)
++ {
++ open_capitem *oc;
++ recno = ng->number;
++ if (is_recurse) break;
++ for (oc = cd->open_caps; oc != NULL; oc = oc->next)
++ {
++ if (oc->number == recno)
++ {
++ oc->flag = TRUE;
++ break;
++ }
++ }
++ }
++ }
++ }
+ }
+
+ /* In the real compile, search the name table. We check the name
+@@ -7262,8 +7275,6 @@ for (;; ptr++)
+ for (i++; i < cd->names_found; i++)
+ {
+ if (STRCMP_UC_UC(slot + IMM2_SIZE, cslot + IMM2_SIZE) != 0) break;
+-
+-
+ count++;
+ cslot += cd->name_entry_size;
+ }
+@@ -9189,6 +9200,7 @@ cd->names_found = 0;
+ cd->name_entry_size = 0;
+ cd->name_table = NULL;
+ cd->dupnames = FALSE;
++cd->dupgroups = FALSE;
+ cd->namedrefcount = 0;
+ cd->start_code = cworkspace;
+ cd->hwm = cworkspace;
+@@ -9223,7 +9235,7 @@ if (errorcode != 0) goto PCRE_EARLY_ERROR_RETURN;
+
+ DPRINTF(("end pre-compile: length=%d workspace=%d\n", length,
+ (int)(cd->hwm - cworkspace)));
+-
++
+ if (length > MAX_PATTERN_SIZE)
+ {
+ errorcode = ERR20;
+diff --git a/pcre_internal.h b/pcre_internal.h
+index dd0ac7f..7ca6020 100644
+--- a/pcre_internal.h
++++ b/pcre_internal.h
+@@ -2446,6 +2446,7 @@ typedef struct compile_data {
+ BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */
+ BOOL check_lookbehind; /* Lookbehinds need later checking */
+ BOOL dupnames; /* Duplicate names exist */
++ BOOL dupgroups; /* Duplicate groups exist: (?| found */
+ BOOL iscondassert; /* Next assert is a condition */
+ int nltype; /* Newline type */
+ int nllen; /* Newline string length */
+diff --git a/testdata/testinput2 b/testdata/testinput2
+index e12de3a..8e044f8 100644
+--- a/testdata/testinput2
++++ b/testdata/testinput2
+@@ -4158,4 +4158,6 @@ backtracking verbs. --/
+
+ "(?J:(?|(?'R')(\k'R')|((?'R'))))"
+
++/(?J:(?|(:(?|(?'R')(\k'R')|((?'R')))H'Rk'Rf)|s(?'R')))/
++
+ /-- End of testinput2 --/
+diff --git a/testdata/testoutput2 b/testdata/testoutput2
+index 5bad26c..6019425 100644
+--- a/testdata/testoutput2
++++ b/testdata/testoutput2
+@@ -14430,4 +14430,6 @@ Failed: unmatched parentheses at offset 23
+
+ "(?J:(?|(?'R')(\k'R')|((?'R'))))"
+
++/(?J:(?|(:(?|(?'R')(\k'R')|((?'R')))H'Rk'Rf)|s(?'R')))/
++
+ /-- End of testinput2 --/
+--
+2.4.3
+