Bug#1146405: trixie-pu: package pcre2/10.46-1~deb13u2

Matthew Vernon <[email protected]>
Newsgroups gmane.linux.debian.devel.release
Message-ID <178826628754.30998.8204726344654145281.reportbug__7374.58074452064$1788267944$gmane$org@aragorn.weathertop.principate.org.uk>
Package: release.debian.org
Severity: normal
Tags: trixie
User: [email protected]
Usertags: pu
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:pcre2

Hi,

The recent 10.48 release of PCRE2 included a number of security
fixes. Upstream provided backported fixes for 10.46 (the version in
trixie), and the security team think this doesn't warrant a DSA but
would be good to go out in the forthcoming trixie point release, cf:

https://security-tracker.debian.org/tracker/source-package/pcre2

These are fixes provided (and tested) by upstream. The compile-time
tests pass, and I'm additionally getting debusine to run the
autopkgtests of the immediate reverse-dependencies:

https://debusine.debian.net/debian/developers/work-request/1209574/

I attach a source debdiff; it's quite lengthy because it's 11
backported fixes, which you can see separately in the upstream branch:

https://github.com/PCRE2Project/pcre2/compare/main...release/pcre2-10.46

Thanks,

Matthew
pcre2_10.46-1~deb13u2.debdiff (text/plain, 40.2 KB)
diff -u pcre2-10.46/debian/changelog pcre2-10.46/debian/changelog
--- pcre2-10.46/debian/changelog
+++ pcre2-10.46/debian/changelog
@@ -1,3 +1,9 @@
+pcre2 (10.46-1~deb13u2) trixie; urgency=high
+
+  * Use upstream backports of security fixes from 10.48
+
+ -- Matthew Vernon <[email protected]>  Tue, 01 Sep 2026 11:46:58 +0100
+
 pcre2 (10.46-1~deb13u1) trixie; urgency=high
 
   * New upstream release to fix CVE-2025-58050 (Closes: #1112278)
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_compile.c
+++ pcre2-10.46/src/pcre2_compile.c
@@ -6069,7 +6069,8 @@
 
     if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)
       {
-      if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code))
+      if (*lengthptr > OFLOW_MAX ||
+          OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code))
         {
         *errorcodeptr = ERR20;   /* Integer overflow */
         return 0;
@@ -8548,7 +8549,8 @@
     *reqcuflagsptr = reqcuflags;
     if (lengthptr != NULL)
       {
-      if (OFLOW_MAX - *lengthptr < length)
+      if (*lengthptr > MAX_PATTERN_SIZE ||
+          MAX_PATTERN_SIZE - *lengthptr < length)
         {
         *errorcodeptr = ERR20;
         return 0;
@@ -8571,6 +8573,19 @@
     {
     code = *codeptr + 1 + LINK_SIZE + skipunits;
     length += 1 + LINK_SIZE;
+
+    /* Move the accumulated length into *lengthptr, providing the next call to
+    compile_branch with as much space in &length and &code as the first did. */
+
+    if (*lengthptr > MAX_PATTERN_SIZE ||
+        MAX_PATTERN_SIZE - *lengthptr < length)
+      {
+      *errorcodeptr = ERR20;
+      cb->erroroffset = 0;
+      return 0;
+      }
+    *lengthptr += length;
+    length = 0;
     }
   else
     {
@@ -10626,7 +10641,8 @@
 #if defined SUPPORT_WIDE_CHARS
 PCRE2_ASSERT((cb.char_lists_size & 0x3) == 0);
 if (length > MAX_PATTERN_SIZE ||
-    MAX_PATTERN_SIZE - length < (cb.char_lists_size / sizeof(PCRE2_UCHAR)))
+    BYTES2CU(cb.char_lists_size) > MAX_PATTERN_SIZE ||
+    MAX_PATTERN_SIZE - length < BYTES2CU(cb.char_lists_size))
 #else
 if (length > MAX_PATTERN_SIZE)
 #endif
@@ -10650,11 +10666,36 @@
   /* Align to 32 bit first. This ensures the
   allocated area will also be 32 bit aligned. */
   re_blocksize = (PCRE2_SIZE)CLIST_ALIGN_TO(re_blocksize, sizeof(uint32_t));
+#else
+  /* Already 32 bit aligned. */
 #endif
+
+  /* We have bounded the length and BYTES2CU(char_lists_size) to
+  MAX_PATTERN_SIZE units, however (with 32-bit code units) char_lists_size
+  in bytes could still be extremely close to (or greater than) SIZE_MAX, so
+  we require another overflow check. */
+
+  if (cb.char_lists_size > PCRE2_SIZE_MAX - re_blocksize)
+    {
+    errorcode = ERR20;
+    cb.erroroffset = 0;
+    goto HAD_CB_ERROR;
+    }
+
   re_blocksize += cb.char_lists_size;
   }
 #endif
 
+if (length > BYTES2CU(PCRE2_SIZE_MAX - re_blocksize))
+  {
+  /* Given the current value of 2^30 for MAX_PATTERN_SIZE, this block is only
+  reachable when both PCRE2_CODE_UNIT_WIDTH >= 16 and sizeof(size_t) is
+  32 bits. */
+  errorcode = ERR20;
+  cb.erroroffset = 0;
+  goto HAD_CB_ERROR;
+  }
+
 re_blocksize += CU2BYTES(length);
 
 if (re_blocksize > ccontext->max_pattern_compiled_length)
@@ -10663,7 +10704,15 @@
   goto HAD_CB_ERROR;
   }
 
+if (sizeof(pcre2_real_code) > PCRE2_SIZE_MAX - re_blocksize)
+  {
+  errorcode = ERR20;
+  cb.erroroffset = 0;
+  goto HAD_CB_ERROR;
+  }
+
 re_blocksize += sizeof(pcre2_real_code);
+
 re = (pcre2_real_code *)
   ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data);
 if (re == NULL)
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_compile_class.c
+++ pcre2-10.46/src/pcre2_compile_class.c
@@ -499,7 +499,7 @@
 
 static class_ranges *
 compile_optimize_class(uint32_t *start_ptr, uint32_t options,
-  uint32_t xoptions, compile_block *cb)
+  uint32_t xoptions, int *errorcodeptr, compile_block *cb)
 {
 class_ranges* cranges;
 uint32_t *ptr;
@@ -536,12 +536,23 @@
 
 total_size = range_list_size +
    ((range_list_size >= 2) ? CHAR_LIST_EXTRA_SIZE : 0);
+if (total_size > (PCRE2_SIZE_MAX - sizeof(class_ranges)) / sizeof(uint32_t))
+  {
+  *errorcodeptr = ERR20;
+  cb->erroroffset = 0;
+  return NULL;
+  }
 
 cranges = cb->cx->memctl.malloc(
   sizeof(class_ranges) + total_size * sizeof(uint32_t),
   cb->cx->memctl.memory_data);
 
-if (cranges == NULL) return NULL;
+if (cranges == NULL)
+  {
+  *errorcodeptr = ERR21;
+  cb->erroroffset = 0;
+  return NULL;
+  }
 
 cranges->next = NULL;
 cranges->range_list_size = (uint16_t)range_list_size;
@@ -716,9 +727,13 @@
   else
     cranges->char_lists_types |= tmp1 << tmp2;
 
-  if (range_start < XCL_CHAR_LIST_LOW_16_START) break;
+  if (range_end < XCL_CHAR_LIST_LOW_16_START || tmp2 == 0)
+    {
+    PCRE2_ASSERT(range_start < XCL_CHAR_LIST_LOW_16_START);
+    break;
+    }
 
-  PCRE2_ASSERT(tmp2 >= XCL_TYPE_BIT_LEN);
+  PCRE2_ASSERT((tmp2 % XCL_TYPE_BIT_LEN) == 0);
   char_list_end = char_list_start - 1;
   char_list_start = *char_list_next++;
   tmp1 = 0;
@@ -1103,13 +1118,10 @@
   {
   if (lengthptr != NULL)
     {
-    cranges = compile_optimize_class(pptr, options, xoptions, cb);
+    cranges = compile_optimize_class(pptr, options, xoptions, errorcodeptr, cb);
 
     if (cranges == NULL)
-      {
-      *errorcodeptr = ERR21;
       return NULL;
-      }
 
     /* Caching the pre-processed character ranges. */
     if (cb->next_cranges != NULL)
@@ -1727,18 +1739,17 @@
       *lengthptr += 1 + LINK_SIZE;
 #endif
 
-      cb->char_lists_size += char_lists_size;
+      PCRE2_ASSERT(BYTES2CU(cb->char_lists_size) <= MAX_PATTERN_SIZE);
 
-      char_lists_size /= sizeof(PCRE2_UCHAR);
-
-      /* Storage space for character lists is included
-      in the maximum pattern size. */
-      if (*lengthptr > MAX_PATTERN_SIZE ||
-          MAX_PATTERN_SIZE - *lengthptr < char_lists_size)
+      if (char_lists_size > PCRE2_SIZE_MAX - cb->char_lists_size ||
+          BYTES2CU(char_lists_size) > MAX_PATTERN_SIZE ||
+          BYTES2CU(cb->char_lists_size) > MAX_PATTERN_SIZE - BYTES2CU(char_lists_size))
         {
         *errorcodeptr = ERR20;   /* Pattern is too large */
         return NULL;
         }
+
+      cb->char_lists_size += char_lists_size;
       }
     else
       {
@@ -1761,6 +1772,8 @@
       Each list is aligned to 32 bit with an optional unused
       16 bit value at the beginning of the character list. */
 
+      PCRE2_ASSERT(char_lists_size <= PCRE2_SIZE_MAX - cb->char_lists_size);
+
       cb->char_lists_size += char_lists_size;
       data = (uint8_t*)cb->start_code - cb->char_lists_size;
 
@@ -1774,17 +1787,14 @@
       PUT(code, 0, (uint32_t)(char_lists_size >> 1));
       code += LINK_SIZE;
 
-#if defined PCRE2_DEBUG || defined SUPPORT_VALGRIND
+      /* If we added padding to align the list, initialize the bytes to
+      defined values, so the library is valgrind-clean. It could also
+      be a security concern for clients calling into PCRE2 via bindings
+      from a memory-safe language, if pcre2_serialize_encode() exposes
+      uninitialized memory that may contain sensitive information. */
+
       if ((char_lists_size & 0x2) != 0)
-        {
-        /* In debug the unused 16 bit value is set
-        to a fixed value and marked unused. */
-        ((uint16_t*)data)[-1] = 0x5555;
-#ifdef SUPPORT_VALGRIND
-        VALGRIND_MAKE_MEM_NOACCESS(data - 2, 2);
-#endif
-        }
-#endif
+        ((uint16_t*)data)[-1] = 0xdead;
 
       cb->char_lists_size =
         CLIST_ALIGN_TO(char_lists_size, sizeof(uint32_t));
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_convert.c
+++ pcre2-10.46/src/pcre2_convert.c
@@ -1148,9 +1148,12 @@
   /* Allocate memory for the buffer, with hidden space for an allocator at
   the start. The next time round the loop runs the conversion for real. */
 
-  allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
-    (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext);
-  if (allocated == NULL) return PCRE2_ERROR_NOMEMORY;
+  if (*bufflenptr > ((PCRE2_SIZE_MAX - sizeof(pcre2_memctl)) /
+        CU2BYTES(1)) - 1 ||
+      (allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+        CU2BYTES(*bufflenptr + 1),
+        (pcre2_memctl *)ccontext)) == NULL)
+    return PCRE2_ERROR_NOMEMORY;
   *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl));
 
   use_buffer = *buffptr;
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_dfa_match.c
+++ pcre2-10.46/src/pcre2_dfa_match.c
@@ -407,8 +407,8 @@
 
 /* This function is called when internal_dfa_match() is about to be called
 recursively and there is insufficient working space left in the current
-workspace block. If there's an existing next block, use it; otherwise get a new
-block unless the heap limit is reached.
+workspace block. If there's a sufficiently large next block, use it; get a new
+block unless the heap limit is (or has been) reached.
 
 Arguments:
   rwsptr     pointer to block pointer (updated)
@@ -424,9 +424,18 @@
 {
 RWS_anchor *rws = *rwsptr;
 RWS_anchor *new;
+uint32_t requested;
+
+PCRE2_ASSERT(ovecsize <= UINT32_MAX - RWS_RSIZE - RWS_ANCHOR_SIZE);
+requested = RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE;
 
 if (rws->next != NULL)
   {
+  /* Although the initial block is large, and subsequent ones try to double, the
+  heap limit may cause the last one to be smaller; in this case, we have already
+  hit the heap limit and allocating a larger block will not be possible. */
+  if (rws->next->size < requested)
+    return PCRE2_ERROR_HEAPLIMIT;
   new = rws->next;
   }
 
@@ -436,14 +445,30 @@
 
 else
   {
-  uint32_t newsize = (rws->size >= UINT32_MAX/(sizeof(int)*2))? UINT32_MAX/sizeof(int) : rws->size * 2;
+  uint32_t newsize = (rws->size >= (UINT32_MAX/sizeof(int))/2)?
+    UINT32_MAX/sizeof(int) : rws->size * 2;
   uint32_t newsizeK = newsize/(1024/sizeof(int));
 
-  if (newsizeK + mb->heap_used > mb->heap_limit)
-    newsizeK = (uint32_t)(mb->heap_limit - mb->heap_used);
-  newsize = newsizeK*(1024/sizeof(int));
+  /* Clamp the allocation to the remaining heap allowance with care for overflows */
+
+  if (mb->heap_used >= mb->heap_limit)
+    {
+    newsize = 0;
+    newsizeK = 0;
+    }
+  else
+    {
+    PCRE2_SIZE availableK = mb->heap_limit - mb->heap_used;
+    /* newsize always capped at UINT32_MAX/sizeof(int), so newsizeK also capped;
+    and - if availableK is smaller - then multiplication to form newsize is safe */
+    if (newsizeK > availableK)
+      {
+      newsize = (uint32_t)(availableK*(1024/sizeof(int)));
+      newsizeK = availableK;
+      }
+    }
 
-  if (newsize < RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE)
+  if (newsize < requested)
     return PCRE2_ERROR_HEAPLIMIT;
   new = mb->memctl.malloc(newsize*sizeof(int), mb->memctl.memory_data);
   if (new == NULL) return PCRE2_ERROR_NOMEMORY;
@@ -2803,6 +2828,7 @@
 
         local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
         local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+        PCRE2_ASSERT(rws->free >= RWS_RSIZE + RWS_OVEC_OSIZE);
         rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
 
         while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
@@ -2902,6 +2928,7 @@
 
           local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
           local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+          PCRE2_ASSERT(rws->free >= RWS_RSIZE + RWS_OVEC_OSIZE);
           rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
 
           while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
@@ -2950,6 +2977,7 @@
 
         local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
         local_workspace = ((int *)local_offsets) + RWS_OVEC_RSIZE;
+        PCRE2_ASSERT(rws->free >= RWS_RSIZE + RWS_OVEC_RSIZE);
         rws->free -= RWS_RSIZE + RWS_OVEC_RSIZE;
 
         /* Check for repeating a recursion without advancing the subject
@@ -3049,6 +3077,7 @@
 
         local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
         local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+        PCRE2_ASSERT(rws->free >= RWS_RSIZE + RWS_OVEC_OSIZE);
         rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
 
         if (codevalue == OP_BRAPOSZERO)
@@ -3148,6 +3177,7 @@
 
         local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
         local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+        PCRE2_ASSERT(rws->free >= RWS_RSIZE + RWS_OVEC_OSIZE);
         rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
 
         rc = internal_dfa_match(
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_extuni.c
+++ pcre2-10.46/src/pcre2_extuni.c
@@ -57,12 +57,12 @@
 
 #ifndef SUPPORT_UNICODE
 PCRE2_SPTR
-PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR check_subject,
   PCRE2_SPTR end_subject, BOOL utf, int *xcount)
 {
 (void)c;
 (void)eptr;
-(void)start_subject;
+(void)check_subject;
 (void)end_subject;
 (void)utf;
 (void)xcount;
@@ -83,7 +83,7 @@
 Arguments:
   c              the first character
   eptr           pointer to next character
-  start_subject  pointer to start of subject
+  check_subject  pointer to start of validated subject
   end_subject    pointer to end of subject
   utf            TRUE if in UTF mode
   xcount         pointer to count of additional characters,
@@ -93,7 +93,7 @@
 */
 
 PCRE2_SPTR
-PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR check_subject,
   PCRE2_SPTR end_subject, BOOL utf, int *xcount)
 {
 BOOL was_ep_ZWJ = FALSE;
@@ -124,7 +124,7 @@
 
     /* bptr is pointing to the left-hand character */
 
-    while (bptr > start_subject)
+    while (bptr > check_subject)
       {
       bptr--;
       if (utf)
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_internal.h
+++ pcre2-10.46/src/pcre2_internal.h
@@ -1397,7 +1397,7 @@
 The items are ordered in increasing order, so binary search can be
 used to find the lower bound of an input character. The lower bound
 is the highest item, which value is less or equal than the input
-character. If the lower bit of the item is cleard, or the character
+character. If the lower bit of the item is cleared, or the character
 stored in the item equals to the input character, the input
 character is in the character list. */
 
@@ -1418,14 +1418,19 @@
 #define XCL_CHAR_LIST_HIGH_32_END 0xffffffff
 #define XCL_CHAR_LIST_HIGH_32_ADD 0x80000000
 
-/* Mask for getting the descriptors of character list ranges.
-Each descriptor has XCL_TYPE_BIT_LEN bits, and can be processed
-by XCL_BEGIN_WITH_RANGE and XCL_ITEM_COUNT_MASK macros. */
+/* Mask and length values for getting the descriptors of
+all character list ranges. The bit length of each descriptor
+is XCL_TYPE_BIT_LEN so the total size is 4*XCL_TYPE_BIT_LEN
+(currently 12 bit). This data is stored for all four character
+lists, even if no characters are present in a list. */
 #define XCL_TYPE_MASK 0xfff
 #define XCL_TYPE_BIT_LEN 3
-/* If this bit is set, the first item of the character list is the
-end of a range, which started before the starting character of the
-character list. */
+/* If this bit is set for a character class, the first item of the
+character list is the end of a range, which started before the
+starting character of the character list. If this bit is set, and
+no characters are present in the list, the whole character class
+is part of a range. E.g: [\x{500}-\x{12000}] covers the entire
+0x8000-0xffff range. */
 #define XCL_BEGIN_WITH_RANGE 0x4
 /* Number of items in the character list: 0, 1, or 2. The value 3
 represents that the item count is stored at the begining of the
@@ -2170,6 +2175,7 @@
 #define _pcre2_is_newline            PCRE2_SUFFIX(_pcre2_is_newline_)
 #define _pcre2_jit_free_rodata       PCRE2_SUFFIX(_pcre2_jit_free_rodata_)
 #define _pcre2_jit_free              PCRE2_SUFFIX(_pcre2_jit_free_)
+#define _pcre2_jit_check_exec        PCRE2_SUFFIX(_pcre2_jit_check_exec_)
 #define _pcre2_jit_get_size          PCRE2_SUFFIX(_pcre2_jit_get_size_)
 #define _pcre2_jit_get_target        PCRE2_SUFFIX(_pcre2_jit_get_target_)
 #define _pcre2_memctl_malloc         PCRE2_SUFFIX(_pcre2_memctl_malloc_)
@@ -2198,6 +2204,7 @@
                       uint32_t *, BOOL);
 extern void         _pcre2_jit_free_rodata(void *, void *);
 extern void         _pcre2_jit_free(void *, pcre2_memctl *);
+extern BOOL         _pcre2_jit_check_exec(void *, uint32_t);
 extern size_t       _pcre2_jit_get_size(void *);
 const char *        _pcre2_jit_get_target(void);
 extern void *       _pcre2_memctl_malloc(size_t, pcre2_memctl *);
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_jit_compile.c
+++ pcre2-10.46/src/pcre2_jit_compile.c
@@ -6107,6 +6107,7 @@
       repeat = GET2(cc, 1);
       if (repeat <= 0)
         {
+        repeat = 1;
         chars_end = chars;
         continue;
         }
@@ -12230,6 +12231,8 @@
   if (common->restore_end_ptr == 0)
     common->restore_end_ptr = private_data_ptr + sizeof(sljit_sw);
   }
+else if (SLJIT_UNLIKELY(opcode == OP_ASSERTBACK_NA) && PRIVATE_DATA(ccbegin + 1))
+  OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
 
 if (SLJIT_UNLIKELY(opcode == OP_ONCE))
   {
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_jit_match.c
+++ pcre2-10.46/src/pcre2_jit_match.c
@@ -118,6 +118,7 @@
 int rc;
 int index = 0;
 
+/* The same check is performed by jit_check_exec(). */
 if ((options & PCRE2_PARTIAL_HARD) != 0)
   index = 2;
 else if ((options & PCRE2_PARTIAL_SOFT) != 0)
@@ -126,6 +127,16 @@
 if (functions == NULL || functions->executable_funcs[index] == NULL)
   return PCRE2_ERROR_JIT_BADOPTION;
 
+/* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT,
+free the memory that was obtained. */
+
+if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+  {
+  match_data->memctl.free((void *)match_data->subject,
+    match_data->memctl.memory_data);
+  match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT;
+  }
+
 /* Sanity checks should be handled by pcre2_match. */
 arguments.str = subject + start_offset;
 arguments.begin = subject;
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_jit_misc.c
+++ pcre2-10.46/src/pcre2_jit_misc.c
@@ -215,6 +215,32 @@
 
 
 /*************************************************
+*          Checks function compilation           *
+*************************************************/
+
+BOOL
+PRIV(jit_check_exec)(void *executable_jit, uint32_t options)
+{
+#ifndef SUPPORT_JIT
+(void)executable_jit;
+(void)options;
+return FALSE;
+#else  /* SUPPORT_JIT */
+/* The same check is performed at the beginning of pcre2_jit_match(). */
+executable_functions *functions = (executable_functions *)executable_jit;
+int index = 0;
+
+if ((options & PCRE2_PARTIAL_HARD) != 0)
+  index = 2;
+else if ((options & PCRE2_PARTIAL_SOFT) != 0)
+  index = 1;
+
+return functions->executable_funcs[index] != NULL;
+#endif
+}
+
+
+/*************************************************
 *              Get size of JIT code              *
 *************************************************/
 
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_jit_test.c
+++ pcre2-10.46/src/pcre2_jit_test.c
@@ -277,6 +277,7 @@
 	{ CM, A, 0, 0, "a1277|a1377|bx487", "bX487" },
 	{ 0, A, 0, 0, "(a|)b*+a", "a" },
 	{ 0, A, 0, 0 | F_NOMATCH, "(.|.|.|.|.)(|.|.|.|.)(.||.|.|.)(.|.||.|.)(.|.|.||.)(.|.|.|.|)(A|.|.|.|.)(.|A|.|.|.)(.|.|A|.|.)(.|.|.|A|.)(.|.|.|.|A)(B|.|.|.|.)(.|B|.|.|.)(.|.|B|.|.)(.|.|.|B|.)(.|.|.|.|B)xa", "1234567890123456ax" },
+	{ 0, A, 0, 0, "(CHAN|LINE)[ab]{0,2}", "LINE" },
 
 	/* Greedy and non-greedy ? operators. */
 	{ MU, A, 0, 0, "(?:a)?a", "laab" },
@@ -651,6 +652,7 @@
 	{ MU, A, 0, 0, "a(?=)b", "ab" },
 	{ MU, A, 0, 0 | F_NOMATCH, "a(?!)b", "ab" },
 	{ MU, A, 0, 0, "(?(?<!|(|a)))", "a" },
+	{ MU, A, 0, 3, "(?<*(.).{,2})\\1", "BABA" },
 
 	/* Not empty, ACCEPT, FAIL */
 	{ MU, A, PCRE2_NOTEMPTY, 0 | F_NOMATCH, "a*", "bcx" },
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_match.c
+++ pcre2-10.46/src/pcre2_match.c
@@ -2802,7 +2802,7 @@
     else
       {
       GETCHARINCTEST(fc, Feptr);
-      Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf,
+      Feptr = PRIV(extuni)(fc, Feptr, mb->check_subject, mb->end_subject, utf,
         NULL);
       }
     CHECK_PARTIAL();
@@ -3151,7 +3151,7 @@
           else
             {
             GETCHARINCTEST(fc, Feptr);
-            Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject,
+            Feptr = PRIV(extuni)(fc, Feptr, mb->check_subject,
               mb->end_subject, utf, NULL);
             }
           CHECK_PARTIAL();
@@ -3969,7 +3969,7 @@
           else
             {
             GETCHARINCTEST(fc, Feptr);
-            Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+            Feptr = PRIV(extuni)(fc, Feptr, mb->check_subject, mb->end_subject,
               utf, NULL);
             }
           CHECK_PARTIAL();
@@ -4552,7 +4552,7 @@
           else
             {
             GETCHARINCTEST(fc, Feptr);
-            Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+            Feptr = PRIV(extuni)(fc, Feptr, mb->check_subject, mb->end_subject,
               utf, NULL);
             }
           CHECK_PARTIAL();
@@ -6123,7 +6123,7 @@
       {
       for (i = 0; i < Lmax; i++)
         {
-        if (Feptr == mb->start_subject)
+        if (Feptr <= mb->check_subject)
           {
           if (i < Lmin) RRETURN(MATCH_NOMATCH);
           Lmax = i;
@@ -6867,10 +6867,6 @@
 PCRE2_SPTR start_partial;
 PCRE2_SPTR match_partial;
 
-#ifdef SUPPORT_JIT
-BOOL use_jit;
-#endif
-
 /* This flag is needed even when Unicode is not supported for convenience
 (it is used by the IS_NEWLINE macro). */
 
@@ -6880,9 +6876,6 @@
 BOOL ucp = FALSE;
 BOOL allow_invalid;
 uint32_t fragment_options = 0;
-#ifdef SUPPORT_JIT
-BOOL jit_checked_utf = FALSE;
-#endif
 #endif  /* SUPPORT_UNICODE */
 
 PCRE2_SIZE frame_size;
@@ -6943,15 +6936,6 @@
 #undef FF
 #undef OO
 
-/* If the pattern was successfully studied with JIT support, we will run the
-JIT executable instead of the rest of this function. Most options must be set
-at compile time for the JIT code to be usable. */
-
-#ifdef SUPPORT_JIT
-use_jit = (re->executable_jit != NULL &&
-          (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0);
-#endif
-
 /* Initialize UTF/UCP parameters. */
 
 #ifdef SUPPORT_UNICODE
@@ -6997,20 +6981,25 @@
 
 /* ============================= JIT matching ============================== */
 
-/* Prepare for JIT matching. Check a UTF string for validity unless no check is
-requested or invalid UTF can be handled. We check only the portion of the
-subject that might be be inspected during matching - from the offset minus the
-maximum lookbehind to the given length. This saves time when a small part of a
-large subject is being matched by the use of a starting offset. Note that the
-maximum lookbehind is a number of characters, not code units. */
+/* If the pattern was successfully studied with JIT support, we will run the
+JIT executable instead of the rest of this function. Most options must be set
+at compile time for the JIT code to be usable. */
 
 #ifdef SUPPORT_JIT
-if (use_jit)
+if (re->executable_jit != NULL &&
+    (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0 &&
+    PRIV(jit_check_exec)(re->executable_jit, options))
   {
+  /* Prepare for JIT matching. Check a UTF string for validity unless no check
+  is requested or invalid UTF can be handled. We check only the portion of the
+  subject that might be be inspected during matching - from the offset minus
+  the maximum lookbehind to the given length. This saves time when a small part
+  of a large subject is being matched by the use of a starting offset. Note that
+  the maximum lookbehind is a number of characters, not code units. */
+
 #ifdef SUPPORT_UNICODE
   if (utf && (options & PCRE2_NO_UTF_CHECK) == 0 && !allow_invalid)
     {
-
     /* For 8-bit and 16-bit UTF, check that the first code unit is a valid
     character start. */
 
@@ -7063,16 +7052,14 @@
       match_data->startchar += start_match - subject;
       return match_data->rc;
       }
-    jit_checked_utf = TRUE;
     }
 #endif  /* SUPPORT_UNICODE */
 
-  /* If JIT returns BADOPTION, which means that the selected complete or
-  partial matching mode was not compiled, fall through to the interpreter. */
-
   rc = pcre2_jit_match(code, subject, length, start_offset, options,
     match_data, mcontext);
-  if (rc != PCRE2_ERROR_JIT_BADOPTION)
+  /* JIT must be able to perform the match. */
+  PCRE2_ASSERT(rc != PCRE2_ERROR_JIT_BADOPTION);
+
     {
     match_data->subject_length = length;
     if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
@@ -7098,12 +7085,8 @@
 
 mb->check_subject = subject;
 
-/* If a UTF subject string was not checked for validity in the JIT code above,
-check it here, and handle support for invalid UTF strings. The check above
-happens only when invalid UTF is not supported and PCRE2_NO_CHECK_UTF is unset.
-If we get here in those circumstances, it means the subject string is valid,
-but for some reason JIT matching was not successful. There is no need to check
-the subject again.
+/* Check the validity of UTF subject strings. The check happens only when
+PCRE2_NO_CHECK_UTF is unset.
 
 We check only the portion of the subject that might be be inspected during
 matching - from the offset minus the maximum lookbehind to the given length.
@@ -7115,11 +7098,7 @@
 of PCRE2_NO_CHECK_UTF. */
 
 #ifdef SUPPORT_UNICODE
-if (utf &&
-#ifdef SUPPORT_JIT
-    !jit_checked_utf &&
-#endif
-    ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid))
+if (utf && ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid))
   {
 #if PCRE2_CODE_UNIT_WIDTH != 32
   BOOL skipped_bad_start = FALSE;
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_pattern_info.c
+++ pcre2-10.46/src/pcre2_pattern_info.c
@@ -292,8 +292,7 @@
 if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;
 
 cb.version = 0;
-cc = (PCRE2_SPTR)((const uint8_t *)re + sizeof(pcre2_real_code))
-     + re->name_count * re->name_entry_size;
+cc = (PCRE2_SPTR)((uint8_t *)re + re->code_start);
 
 while (TRUE)
   {
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2_substring.c
+++ pcre2-10.46/src/pcre2_substring.c
@@ -214,9 +214,10 @@
 PCRE2_UCHAR *yield;
 rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);
 if (rc < 0) return rc;
-yield = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
-  (size + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)match_data);
-if (yield == NULL) return PCRE2_ERROR_NOMEMORY;
+if (size > ((PCRE2_SIZE_MAX - sizeof(pcre2_memctl)) / CU2BYTES(1)) - 1 ||
+    (yield = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+      CU2BYTES(size + 1), (pcre2_memctl *)match_data)) == NULL)
+  return PCRE2_ERROR_NOMEMORY;
 yield = (PCRE2_UCHAR *)(((char *)yield) + sizeof(pcre2_memctl));
 memcpy(yield, match_data->subject + match_data->ovector[stringnumber*2],
   CU2BYTES(size));
only in patch2:
unchanged:
--- pcre2-10.46.orig/src/pcre2test.c
+++ pcre2-10.46/src/pcre2test.c
@@ -512,7 +512,6 @@
 /* Combinations */
 
 #define CTL_DEBUG            (CTL_FULLBINCODE|CTL_INFO)  /* For setting */
-#define CTL_ANYINFO          (CTL_DEBUG|CTL_BINCODE|CTL_CALLOUT_INFO)
 #define CTL_ANYGLOB          (CTL_ALTGLOBAL|CTL_GLOBAL)
 
 /* Second control word */
@@ -4652,6 +4651,16 @@
 return 0;
 }
 
+/* Backport from 10.47 */
+
+static int callout_enumerate_function_void(pcre2_callout_enumerate_block_8 *cb,
+  void *callout_data)
+{
+(void)cb;
+(void)callout_data;
+return 0;
+}
+
 
 
 /*************************************************
@@ -4671,9 +4680,16 @@
 static int
 show_pattern_info(void)
 {
+int rc_enum;
 uint32_t compile_options, overall_options, extra_options;
 BOOL utf = (FLD(compiled_code, overall_options) & PCRE2_UTF) != 0;
 
+if ((pat_patctl.control & CTL_MEMORY) != 0)
+  show_memory_info();
+
+if ((pat_patctl.control2 & CTL2_FRAMESIZE) != 0)
+  show_framesize();
+
 if ((pat_patctl.control & (CTL_BINCODE|CTL_FULLBINCODE)) != 0)
   {
   fprintf(outfile, "------------------------------------------------------------------\n");
@@ -5016,17 +5032,17 @@
     }
   }
 
-if ((pat_patctl.control & CTL_CALLOUT_INFO) != 0)
+PCRE2_CALLOUT_ENUMERATE(rc_enum,
+  (((pat_patctl.control & CTL_CALLOUT_INFO) != 0)? callout_callback :
+  /* Exercise the callout enumeration code with a dummy callback to make sure
+  it works. */
+  callout_enumerate_function_void), 0);
+if (rc_enum != 0)
   {
-  int errorcode;
-  PCRE2_CALLOUT_ENUMERATE(errorcode, callout_callback, 0);
-  if (errorcode != 0)
-    {
-    fprintf(outfile, "Callout enumerate failed: error %d: ", errorcode);
-    if (errorcode < 0 && !print_error_message(errorcode, "", "\n"))
-      return PR_ABEND;
-    return PR_SKIP;
-    }
+  fprintf(outfile, "Callout enumerate failed: error %d: ", rc_enum);
+  if (rc_enum < 0 && !print_error_message(rc_enum, "", "\n"))
+    return PR_ABEND;
+  return PR_SKIP;
   }
 
 return PR_OK;
@@ -5230,13 +5246,9 @@
     {
     PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit);
     }
-  if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info();
-  if ((pat_patctl.control2 & CTL2_FRAMESIZE) != 0) show_framesize();
-  if ((pat_patctl.control & CTL_ANYINFO) != 0)
-    {
-    rc = show_pattern_info();
-    if (rc != PR_OK) return rc;
-    }
+
+  rc = show_pattern_info();
+  if (rc != PR_OK) return rc;
   break;
 
   /* Save the stack of compiled patterns to a file, then empty the stack. */
@@ -5395,13 +5407,17 @@
 uint32_t k;
 uint8_t *p = buffer;
 unsigned int delimiter = *p++;
-int errorcode;
+int rc, errorcode;
 void *use_pat_context;
 void *use_pbuffer = NULL;
 uint32_t use_forbid_utf = forbid_utf;
 PCRE2_SIZE patlen;
 PCRE2_SIZE valgrind_access_length;
 PCRE2_SIZE erroroffset;
+int32_t serialize_rc;
+void *serialize_code;
+uint8_t *serialized_bytes;
+PCRE2_SIZE serialized_size;
 
 /* The perltest.sh script supports only / as a delimiter. */
 
@@ -5724,7 +5740,6 @@
 if ((pat_patctl.control & CTL_POSIX) != 0)
   {
 #ifdef SUPPORT_PCRE2_8
-  int rc;
   int cflags = 0;
   const char *msg = "** Ignored with POSIX interface:";
 #endif
@@ -5931,7 +5946,6 @@
 
 if (pat_patctl.convert_type != CONVERT_UNSET)
   {
-  int rc;
   int convert_return = PR_OK;
   uint32_t convert_options = pat_patctl.convert_type;
   void *converted_pattern;
@@ -6246,13 +6260,31 @@
 
 /* Output code size and other information if requested. */
 
-if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info();
-if ((pat_patctl.control2 & CTL2_FRAMESIZE) != 0) show_framesize();
-if ((pat_patctl.control & CTL_ANYINFO) != 0)
+rc = show_pattern_info();
+if (rc != PR_OK) return rc;
+
+/* Verify that the compiled structure can be serialized without generating
+memory errors. */
+
+serialize_code = PTR(compiled_code);
+PCRE2_SERIALIZE_ENCODE(serialize_rc, &serialize_code, 1, &serialized_bytes,
+  &serialized_size, general_context);
+if (serialize_rc != 1)
   {
-  int rc = show_pattern_info();
-  if (rc != PR_OK) return rc;
+  fprintf(outfile, "** pcre2_serialize_encode() returned %d instead of 1\n",
+    serialize_rc);
+  return PR_ABEND;
+  }
+
+#if defined SUPPORT_VALGRIND
+if (VALGRIND_CHECK_MEM_IS_DEFINED(serialized_bytes, serialized_size) != 0)
+  {
+  fprintf(outfile, "** pcre2_serialize_encode() returned undefined data\n");
+  return PR_ABEND;
   }
+#endif
+
+PCRE2_SERIALIZE_FREE(serialized_bytes);
 
 /* The "push" control requests that the compiled pattern be remembered on a
 stack. This is mainly for testing the serialization functionality. */
@@ -6389,7 +6421,8 @@
       PTR(dat_context), dfa_workspace, DFA_WS_DIMENSION);
     }
 
-  else if ((pat_patctl.control & CTL_JITFAST) != 0)
+  else if ((pat_patctl.control & CTL_JITFAST) != 0 &&
+           (dat_datctl.options & PCRE2_NO_JIT) == 0)
     PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset,
       dat_datctl.options, match_data, PTR(dat_context));
 
@@ -7989,7 +8022,8 @@
 
   if (emoption != 0)
     {
-    if ((pat_patctl.control & CTL_JITFAST) != 0)
+    if ((pat_patctl.control & CTL_JITFAST) != 0 &&
+        (dat_datctl.options & PCRE2_NO_JIT) == 0)
       {
       PCRE2_JIT_MATCH(rc, compiled_code, pp, arg_ulen, dat_datctl.offset,
         dat_datctl.options, match_data, use_dat_context);
@@ -8208,7 +8242,8 @@
         }
       }
 
-    else if ((pat_patctl.control & CTL_JITFAST) != 0)
+    else if ((pat_patctl.control & CTL_JITFAST) != 0 &&
+             (dat_datctl.options & PCRE2_NO_JIT) == 0)
       {
       start_time = clock();
       for (i = 0; i < timeitm; i++)
@@ -8305,7 +8340,8 @@
       }
     else
       {
-      if ((pat_patctl.control & CTL_JITFAST) != 0)
+      if ((pat_patctl.control & CTL_JITFAST) != 0 &&
+          (dat_datctl.options & PCRE2_NO_JIT) == 0)
         PCRE2_JIT_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset,
           dat_datctl.options | g_notempty, match_data, use_dat_context);
       else
@@ -8353,20 +8389,29 @@
     /* If PCRE2_COPY_MATCHED_SUBJECT was set, check that things are as they
     should be, but not for fast JIT, where it isn't supported. */
 
-    if ((dat_datctl.options & PCRE2_COPY_MATCHED_SUBJECT) != 0 &&
-        (pat_patctl.control & CTL_JITFAST) == 0)
+    if ((dat_datctl.options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
       {
-      if ((FLD(match_data, flags) & PCRE2_MD_COPIED_SUBJECT) == 0)
-        fprintf(outfile,
-          "** PCRE2 error: flag not set after copy_matched_subject\n");
-
-      if (CASTFLD(const void *, match_data, subject) == pp)
-        fprintf(outfile,
-          "** PCRE2 error: copy_matched_subject has not copied\n");
-
-      if (memcmp(CASTFLD(const void *, match_data, subject), pp, ulen) != 0)
-        fprintf(outfile,
-          "** PCRE2 error: copy_matched_subject mismatch\n");
+      if ((pat_patctl.control & CTL_JITFAST) != 0 &&
+          (dat_datctl.options & PCRE2_NO_JIT) == 0)
+        {
+        if ((FLD(match_data, flags) & PCRE2_MD_COPIED_SUBJECT) != 0)
+          fprintf(outfile,
+            "** PCRE2 error: flag set after unsupported copy_matched_subject\n");
+        }
+      else
+        {
+        if ((FLD(match_data, flags) & PCRE2_MD_COPIED_SUBJECT) == 0)
+          fprintf(outfile,
+            "** PCRE2 error: flag not set after copy_matched_subject\n");
+
+        if (CASTFLD(const void *, match_data, subject) == pp)
+          fprintf(outfile,
+            "** PCRE2 error: copy_matched_subject has not copied\n");
+
+        if (memcmp(CASTFLD(const void *, match_data, subject), pp, ulen) != 0)
+          fprintf(outfile,
+            "** PCRE2 error: copy_matched_subject mismatch\n");
+        }
       }
 
     /* If this is not the first time round a global loop, check that the
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testinput10
+++ pcre2-10.46/testdata/testinput10
@@ -573,6 +573,16 @@
     AAA\x80BXYZ 
     AAA\x80BBXYZ 
 
+# Backward scans must not enter the invalid prefix before check_subject.
+
+/(?<=a{1,2})X/utf,match_invalid_utf
+\= Expect no match
+    \x80X
+
+/\X\X/utf,match_invalid_utf
+\= Expect no match
+    \x80\x{1f1e6}\x{1f1e7}
+
 # -------------------------------------
 
 /(*UTF)(?=\x{123})/I
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testinput12
+++ pcre2-10.46/testdata/testinput12
@@ -486,6 +486,16 @@
 /(..)(*scs:(1)ab$)/match_invalid_utf
     ab\x{df00}cde         
 
+# Backward scans must not enter the invalid prefix before check_subject.
+
+/(?<=a{1,2})X/utf,match_invalid_utf
+\= Expect no match
+    \x{dc00}X
+
+/\X\X/utf,match_invalid_utf
+\= Expect no match
+    \x{dc00}\x{1f1e6}\x{1f1e7}
+
 # ---------------------------------------------------- 
 
 /(*UTF)(?=\x{123})/I
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testinput17
+++ pcre2-10.46/testdata/testinput17
@@ -294,8 +294,13 @@
     
 /abc/jitfast
     abc
+    abc\=copy_matched_subject
     abc\=no_jit 
-    
+
+/abc/jitfast
+    abc\=copy_matched_subject,no_jit
+    abc
+
 # ---- 
 
 /[aC]/mg,firstline,newline=lf
@@ -313,4 +318,12 @@
 /(...)-(...)/jitfast
     abc-xyz\=get=2
 
+# Commented-out test; please re-enable and run manually on Unicode builds
+# #if unicode
+#
+# /\b/B,utf,ucp,jit=1
+#     \xff\x00\x00\=offset=2,ps
+#
+# #endif
+
 # End of testinput17
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testinput5
+++ pcre2-10.46/testdata/testinput5
@@ -2882,6 +2882,12 @@
 /([\x{6535}\x{6536}\x{6538}\x{6539}\x{653b}\x{653c}\x{653e}\x{653f}\x{6541}\x{6542}\x{8000}-\x{ffff}]#)+/B,utf
   \x{6534}#\x{6537}#\x{653a}#\x{653d}#\x{6540}#\x{6543}#\x{7fff}#\x{6535}#\x{6536}#\x{6538}#\x{6539}#\x{653b}#\x{653c}#\x{653e}#\x{653f}#\x{6541}#\x{6542}#\x{8000}#\x{c246}#\x{ffff}
 
+/[\x{ff}\x{100}\x{8000}\x{8002}\x{8004}\x{8006}\x{8008}\x{800a}\x{800c}\x{800e}]+/B,utf
+  \x{ff}\x{100}\x{8000}\x{800a}\x{800e}\x{101}
+
+/[\x{ff}-\x{104}\x{8000}\x{8002}\x{8004}\x{8006}\x{8008}\x{800a}\x{800c}\x{800e}]+/B,utf
+  \x{ff}\x{100}\x{101}\x{104}\x{8000}\x{800a}\x{800e}\x{105}
+
 /[[:xdigit:]\x{400}-\x{600}]+/utf,ucp
   !a0\x{400}\x{600}9\x{3ff}
 
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testinput6
+++ pcre2-10.46/testdata/testinput6
@@ -5200,4 +5200,9 @@
 
 # --------------
 
+# Test workspace resizing and workspace re-use
+
+/(*LIMIT_HEAP=4)(?=(?=(?=(?=(?=(?=(?=(?=a))(?R)))))))./
+    a\=dfa
+
 # End of testinput6
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testoutput10
+++ pcre2-10.46/testdata/testoutput10
@@ -1745,6 +1745,18 @@
     AAA\x80BBXYZ 
 No match
 
+# Backward scans must not enter the invalid prefix before check_subject.
+
+/(?<=a{1,2})X/utf,match_invalid_utf
+\= Expect no match
+    \x80X
+No match
+
+/\X\X/utf,match_invalid_utf
+\= Expect no match
+    \x80\x{1f1e6}\x{1f1e7}
+No match
+
 # -------------------------------------
 
 /(*UTF)(?=\x{123})/I
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testoutput12-16
+++ pcre2-10.46/testdata/testoutput12-16
@@ -1626,6 +1626,18 @@
  0: ab
  1: ab
 
+# Backward scans must not enter the invalid prefix before check_subject.
+
+/(?<=a{1,2})X/utf,match_invalid_utf
+\= Expect no match
+    \x{dc00}X
+No match
+
+/\X\X/utf,match_invalid_utf
+\= Expect no match
+    \x{dc00}\x{1f1e6}\x{1f1e7}
+No match
+
 # ---------------------------------------------------- 
 
 /(*UTF)(?=\x{123})/I
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testoutput12-32
+++ pcre2-10.46/testdata/testoutput12-32
@@ -1624,6 +1624,18 @@
  0: ab
  1: ab
 
+# Backward scans must not enter the invalid prefix before check_subject.
+
+/(?<=a{1,2})X/utf,match_invalid_utf
+\= Expect no match
+    \x{dc00}X
+No match
+
+/\X\X/utf,match_invalid_utf
+\= Expect no match
+    \x{dc00}\x{1f1e6}\x{1f1e7}
+No match
+
 # ---------------------------------------------------- 
 
 /(*UTF)(?=\x{123})/I
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testoutput17
+++ pcre2-10.46/testdata/testoutput17
@@ -539,9 +539,17 @@
 /abc/jitfast
     abc
  0: abc (JIT)
+    abc\=copy_matched_subject
+ 0: abc (JIT)
     abc\=no_jit 
  0: abc (JIT)
-    
+
+/abc/jitfast
+    abc\=copy_matched_subject,no_jit
+ 0: abc (JIT)
+    abc
+ 0: abc (JIT)
+
 # ---- 
 
 /[aC]/mg,firstline,newline=lf
@@ -567,4 +575,12 @@
  2: xyz
  2G xyz (3)
 
+# Commented-out test; please re-enable and run manually on Unicode builds
+# #if unicode
+#
+# /\b/B,utf,ucp,jit=1
+#     \xff\x00\x00\=offset=2,ps
+#
+# #endif
+
 # End of testinput17
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testoutput5
+++ pcre2-10.46/testdata/testoutput5
@@ -6254,6 +6254,26 @@
  0: \x{6535}#\x{6536}#\x{6538}#\x{6539}#\x{653b}#\x{653c}#\x{653e}#\x{653f}#\x{6541}#\x{6542}#\x{8000}#\x{c246}#
  1: \x{c246}#
 
+/[\x{ff}\x{100}\x{8000}\x{8002}\x{8004}\x{8006}\x{8008}\x{800a}\x{800c}\x{800e}]+/B,utf
+------------------------------------------------------------------
+        Bra
+        [\xff\x{100}\x{8000}\x{8002}\x{8004}\x{8006}\x{8008}\x{800a}\x{800c}\x{800e}]++
+        Ket
+        End
+------------------------------------------------------------------
+  \x{ff}\x{100}\x{8000}\x{800a}\x{800e}\x{101}
+ 0: \x{ff}\x{100}\x{8000}\x{800a}\x{800e}
+
+/[\x{ff}-\x{104}\x{8000}\x{8002}\x{8004}\x{8006}\x{8008}\x{800a}\x{800c}\x{800e}]+/B,utf
+------------------------------------------------------------------
+        Bra
+        [\xff\x{100}-\x{104}\x{8000}\x{8002}\x{8004}\x{8006}\x{8008}\x{800a}\x{800c}\x{800e}]++
+        Ket
+        End
+------------------------------------------------------------------
+  \x{ff}\x{100}\x{101}\x{104}\x{8000}\x{800a}\x{800e}\x{105}
+ 0: \x{ff}\x{100}\x{101}\x{104}\x{8000}\x{800a}\x{800e}
+
 /[[:xdigit:]\x{400}-\x{600}]+/utf,ucp
   !a0\x{400}\x{600}9\x{3ff}
  0: a0\x{400}\x{600}9
only in patch2:
unchanged:
--- pcre2-10.46.orig/testdata/testoutput6
+++ pcre2-10.46/testdata/testoutput6
@@ -8164,4 +8164,10 @@
 
 # --------------
 
+# Test workspace resizing and workspace re-use
+
+/(*LIMIT_HEAP=4)(?=(?=(?=(?=(?=(?=(?=(?=a))(?R)))))))./
+    a\=dfa
+Failed: error -63: heap limit exceeded
+
 # End of testinput6
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.