CVE-2022-1587 (C, published 2022-05-16T21:15Z)
Description: value: An out-of-bounds read vulnerability was discovered in the PCRE2 library in the get_recurse_data_length() function of the pcre2_jit_compile.c file. This issue affects recursions in JIT-compiled regular expressions caused by duplicate data transfers.
Severity: MEDIUM (CVSS v3 base score: 9.1)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/PCRE2Project/pcre2
Files changed:
ChangeLog
src/pcre2_jit_compile.c
src/pcre2_jit_test.c
Commit message: Fixed an issue affecting recursions in JIT


Vulnerable code:
int size;
BOOL capture_last_found = FALSE;
    if (common->capture_last_ptr != 0)
      capture_last_found = TRUE;
    if (PRIVATE_DATA(cc) != 0)
      length++;
    length++;
    length += 2;
    if (common->capture_last_ptr != 0)
      capture_last_found = TRUE;
    if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
    length += 2 + 2;
    if (common->capture_last_ptr != 0)
      capture_last_found = TRUE;
    if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
    if (PRIVATE_DATA(cc) != 0)
    if (PRIVATE_DATA(cc) != 0)
    if (PRIVATE_DATA(cc) != 0)
    if (PRIVATE_DATA(cc) != 0)
    if (PRIVATE_DATA(cc) != 0)
    if (PRIVATE_DATA(cc) != 0)
    if (PRIVATE_DATA(cc) != 0)
    if (!control_head_found)
      control_head_found = TRUE;
if (capture_last_found)
  length++;
BOOL setsom_found = FALSE;
BOOL setmark_found = FALSE;
BOOL capture_last_found = FALSE;
BOOL control_head_found = FALSE;
control_head_found = TRUE;
    if (has_quit && !setsom_found)
      setsom_found = TRUE;
      if (common->has_set_som && !setsom_found)
        setsom_found = TRUE;
      if (common->mark_ptr != 0 && !setmark_found)
        setmark_found = TRUE;
    if (common->capture_last_ptr != 0 && !capture_last_found)
      capture_last_found = TRUE;
    if (PRIVATE_DATA(cc) != 0)
      private_count = 1;
      private_srcw[0] = PRIVATE_DATA(cc);
    private_count = 1;
    offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
    shared_srcw[0] = OVECTOR(offset);
    shared_srcw[1] = OVECTOR(offset + 1);
    shared_count = 2;
    if (common->capture_last_ptr != 0 && !capture_last_found)
      shared_srcw[2] = common->capture_last_ptr;
      shared_count = 3;
      capture_last_found = TRUE;
    if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
      private_count = 1;
      private_srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
    offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
    shared_srcw[0] = OVECTOR(offset);
    shared_srcw[1] = OVECTOR(offset + 1);
    shared_count = 2;
    if (common->capture_last_ptr != 0 && !capture_last_found)
      shared_srcw[2] = common->capture_last_ptr;
      shared_count = 3;
      capture_last_found = TRUE;
    private_count = 2;
    private_srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
      private_count = 1;
    if (PRIVATE_DATA(cc))
      {
      private_srcw[0] = PRIVATE_DATA(cc);
      }
    if (PRIVATE_DATA(cc))
      private_srcw[0] = PRIVATE_DATA(cc);
      private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
    if (PRIVATE_DATA(cc))
      private_srcw[0] = PRIVATE_DATA(cc);
      private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
    if (PRIVATE_DATA(cc))
      {
      private_srcw[0] = PRIVATE_DATA(cc);
      }
    if (PRIVATE_DATA(cc))
      private_srcw[0] = PRIVATE_DATA(cc);
    if (PRIVATE_DATA(cc))
      private_srcw[0] = PRIVATE_DATA(cc);
        private_count = 1;
        private_count = 2;
        private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
    if (has_quit && !setmark_found)
      setmark_found = TRUE;
    if (common->control_head_ptr != 0 && !control_head_found)
      control_head_found = TRUE;
    if (!control_head_found)
      control_head_found = TRUE;
    break;
common->private_data_ptrs = (sljit_s32 *)SLJIT_MALLOC(total_length * (sizeof(sljit_s32) + (common->has_then ? 1 : 0)), allocator_data);
while (common->currententry != NULL)
  /* Might add new entries. */
  compile_recurse(common);
  if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
  flush_stubs(common);
  common->currententry = common->currententry->next;

Fixed code:
24. Fixed an issue affecting recursions in JIT caused by duplicated data
transfers.

  /* Variables used by recursive call generator. */
  sljit_s32 recurse_bitset_size;
  uint8_t *recurse_bitset;
static BOOL recurse_check_bit(compiler_common *common, sljit_sw bit_index)
{
uint8_t *byte;
uint8_t mask;

SLJIT_ASSERT((bit_index & (sizeof(sljit_sw) - 1)) == 0);

bit_index >>= SLJIT_WORD_SHIFT;

mask = 1 << (bit_index & 0x7);
byte = common->recurse_bitset + (bit_index >> 3);

if (*byte & mask)
  return FALSE;

*byte |= mask;
return TRUE;
}

int size, offset;
memset(common->recurse_bitset, 0, common->recurse_bitset_size);

    if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
      length++;
    offset = PRIVATE_DATA(cc);
    if (offset != 0)
      if (recurse_check_bit(common, offset))
        length++;
    if (recurse_check_bit(common, PRIVATE_DATA(cc)))
      length++;
    offset = GET2(cc, 1 + LINK_SIZE);
    if (recurse_check_bit(common, OVECTOR(offset << 1)))
      {
      SLJIT_ASSERT(recurse_check_bit(common, OVECTOR((offset << 1) + 1)));
      length += 2;
      }
    if (common->optimized_cbracket[offset] == 0 && recurse_check_bit(common, OVECTOR_PRIV(offset)))
      length++;
    if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
    offset = GET2(cc, 1 + LINK_SIZE);
    if (recurse_check_bit(common, OVECTOR(offset << 1)))
      {
      SLJIT_ASSERT(recurse_check_bit(common, OVECTOR((offset << 1) + 1)));
      length += 2;
      }
    if (recurse_check_bit(common, OVECTOR_PRIV(offset)))
      length++;
    if (recurse_check_bit(common, PRIVATE_DATA(cc)))
      length++;
    if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
      length++;
    if ((*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) && recurse_check_bit(common, PRIVATE_DATA(cc)))
    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
      {
      SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
      }
    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
      {
      SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
      }
    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
      {
      SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
      }
    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
      {
      SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
      }

    offset = PRIVATE_DATA(cc);
    if (offset != 0 && recurse_check_bit(common, offset))
    control_head_found = TRUE;

memset(common->recurse_bitset, 0, common->recurse_bitset_size);
recurse_check_bit(common, common->control_head_ptr);
    if (has_quit && recurse_check_bit(common, OVECTOR(0)))
      if (common->has_set_som && recurse_check_bit(common, OVECTOR(0)))
      if (common->mark_ptr != 0 && recurse_check_bit(common, common->mark_ptr))
    if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0)
      if (recurse_check_bit(common, private_srcw[0]))
        private_count = 1;
    if (recurse_check_bit(common, private_srcw[0]))
      private_count = 1;
    offset = GET2(cc, 1 + LINK_SIZE);
    shared_srcw[0] = OVECTOR(offset << 1);
    if (recurse_check_bit(common, shared_srcw[0]))
      {
      shared_srcw[1] = shared_srcw[0] + sizeof(sljit_sw);
      SLJIT_ASSERT(recurse_check_bit(common, shared_srcw[1]));
      shared_count = 2;
      }
    if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
      shared_srcw[shared_count] = common->capture_last_ptr;
      shared_count++;
    if (common->optimized_cbracket[offset] == 0)
      private_srcw[0] = OVECTOR_PRIV(offset);
      if (recurse_check_bit(common, private_srcw[0]))
        private_count = 1;

    offset = GET2(cc, 1 + LINK_SIZE);
    shared_srcw[0] = OVECTOR(offset << 1);
    if (recurse_check_bit(common, shared_srcw[0]))
      {
      shared_srcw[1] = shared_srcw[0] + sizeof(sljit_sw);
      SLJIT_ASSERT(recurse_check_bit(common, shared_srcw[1]));
      shared_count = 2;
      }
    if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
      shared_srcw[shared_count] = common->capture_last_ptr;
      shared_count++;
    if (recurse_check_bit(common, private_srcw[0]))
      private_count = 1;

    offset = OVECTOR_PRIV(offset);
    if (recurse_check_bit(common, offset))
      {
      private_srcw[private_count] = offset;
      private_count++;
      }
      if (recurse_check_bit(common, private_srcw[0]))
        private_count = 1;
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
      private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
      SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
      private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
      SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
      SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
    private_srcw[0] = PRIVATE_DATA(cc);
    if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
      SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
        if (recurse_check_bit(common, private_srcw[0]))
          {
          private_count = 2;
          private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
          SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
          }
    if (has_quit && recurse_check_bit(common, common->mark_ptr))
    if (common->control_head_ptr != 0 && recurse_check_bit(common, common->control_head_ptr))
    if (recurse_check_bit(common, common->control_head_ptr))
    continue;
common->private_data_ptrs = (sljit_s32*)SLJIT_MALLOC(total_length * (sizeof(sljit_s32) + (common->has_then ? 1 : 0)), allocator_data);
SLJIT_ASSERT((private_data_size & (sizeof(sljit_sw) - 1)) == 0);
if (common->currententry != NULL)
  /* A free bit for each private data. */
  common->recurse_bitset_size = ((private_data_size / (int)sizeof(sljit_sw)) + 7) >> 3;
  SLJIT_ASSERT(common->recurse_bitset_size > 0);
  common->recurse_bitset = (sljit_u8*)SLJIT_MALLOC(common->recurse_bitset_size, allocator_data);;

  if (common->recurse_bitset != NULL)
    {
    do
      {
      /* Might add new entries. */
      compile_recurse(common);
      if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
        break;
      flush_stubs(common);
      common->currententry = common->currententry->next;
      }
    while (common->currententry != NULL);

    SLJIT_FREE(common->recurse_bitset, allocator_data);
    }

  if (common->currententry != NULL)
    /* The common->recurse_bitset has been freed. */
    SLJIT_ASSERT(sljit_get_compiler_error(compiler) || common->recurse_bitset == NULL);

	{ MU, A, 0, 0 | F_NOMATCH, "(?1)$((.|\\2xx){1,2})", "abc" },