Commit message: Fix bug #77418 - Heap overflow in utf32be_mbc_to_code

(cherry picked from commit aeec40cb50eca6a97975765e2bacc28a5950cfa9)
CVE: CVE-2019-9023
Vulnerability description: An issue was discovered in PHP before 5.6.40, 7.x before 7.1.26, 7.2.x before 7.2.14, and 7.3.x before 7.3.1. A number of heap-based buffer over-read instances are present in mbstring regular expression functions when supplied with invalid multibyte data. These occur in ext/mbstring/oniguruma/regcomp.c, ext/mbstring/oniguruma/regexec.c, ext/mbstring/oniguruma/regparse.c, ext/mbstring/oniguruma/enc/unicode.c, and ext/mbstring/oniguruma/src/utf32_be.c when a multibyte regular expression pattern contains invalid multibyte sequences.

Project: php-src
File: utf32_le.c

utf32le_mbc_case_fold(OnigCaseFoldType flag,
		      const UChar** pp, const UChar* end, UChar* fold)
{
  const UChar* p = *pp;

  if (ONIGENC_IS_ASCII_CODE(*p) && *(p+1) == 0 && *(p+2) == 0 && *(p+3) == 0) {
#ifdef USE_UNICODE_CASE_FOLD_TURKISH_AZERI
    if ((flag & ONIGENC_CASE_FOLD_TURKISH_AZERI) != 0) {
      if (*p == 0x49) {
        *fold++ = 0x31;
        *fold++ = 0x01;
      }
    }
    else {
#endif
      *fold++ = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*p);
      *fold++ = 0;
#ifdef USE_UNICODE_CASE_FOLD_TURKISH_AZERI
    }
#endif

    *fold++ = 0;
    *fold   = 0;
    *pp += 4;
    return 4;
  }
  else
    return onigenc_unicode_mbc_case_fold(ONIG_ENCODING_UTF32_LE, flag, pp, end,
                                         fold);
}

Project: php-src
File: utf32_be.c

utf32be_is_mbc_newline(const UChar* p, const UChar* end)
{
  if (p + 3 < end) {
    if (*(p+3) == 0x0a && *(p+2) == 0 && *(p+1) == 0 && *p == 0)
      return 1;
#ifdef USE_UNICODE_ALL_LINE_TERMINATORS
    if ((
#ifndef USE_CRNL_AS_LINE_TERMINATOR
         *(p+3) == 0x0d ||
#endif
         *(p+3) == 0x85)
        && *(p+2) == 0 && *(p+1) == 0 && *p == 0x00)
      return 1;

    if (*(p+2) == 0x20 && (*(p+3) == 0x29 || *(p+3) == 0x28)
        && *(p+1) == 0 && *p == 0)
      return 1;
#endif
  }
  return 0;
}

Project: php-src
File: utf32_le.c

utf32le_is_mbc_newline(const UChar* p, const UChar* end)
{
  if (p + 3 < end) {
    if (*p == 0x0a && *(p+1) == 0 && *(p+2) == 0 && *(p+3) == 0)
      return 1;
#ifdef USE_UNICODE_ALL_LINE_TERMINATORS
    if ((
#ifndef USE_CRNL_AS_LINE_TERMINATOR
         *p == 0x0d ||
#endif
         *p == 0x85)
        && *(p+1) == 0x00 && (p+2) == 0x00 && *(p+3) == 0x00)
      return 1;

    if (*(p+1) == 0x20 && (*p == 0x29 || *p == 0x28)
        && *(p+2) == 0x00 && *(p+3) == 0x00)
      return 1;
#endif
  }
  return 0;
}

Project: php-src
File: utf32_le.c

utf32le_mbc_to_code(const UChar* p, const UChar* end ARG_UNUSED)
{
  if (end - p < 4) return 0;
  return (OnigCodePoint )(((p[3] * 256 + p[2]) * 256 + p[1]) * 256 + p[0]);
}

Project: php-src
File: utf16_le.c

init(void)
{
#ifdef USE_CALLOUT

    int id;
    OnigEncoding enc;
    char* name;
    unsigned int args[4];
    OnigValue    opts[4];

    enc = ONIG_ENCODING_UTF16_LE;

    name = "F\000A\000I\000L\000\000\000";            BC0_P(name, fail);
    name = "M\000I\000S\000M\000A\000T\000C\000H\000\000\000"; BC0_P(name, mismatch);

    name = "M\000A\000X\000\000\000";
    args[0] = ONIG_TYPE_TAG | ONIG_TYPE_LONG;
    args[1] = ONIG_TYPE_CHAR;
    opts[0].c = 'X';
    BC_B_O(name, max, 2, args, 1, opts);

    name = "E\000R\000R\000O\000R\000\000\000";
    args[0] = ONIG_TYPE_LONG; opts[0].l = ONIG_ABORT;
    BC_P_O(name, error, 1, args, 1, opts);

    name = "C\000O\000U\000N\000T\000\000\000";
    args[0] = ONIG_TYPE_CHAR; opts[0].c = '>';
    BC_B_O(name, count, 1, args, 1, opts);

    name = "T\000O\000T\000A\000L\000_\000C\000O\000U\000N\000T\000\000\000";
    args[0] = ONIG_TYPE_CHAR; opts[0].c = '>';
    BC_B_O(name, total_count, 1, args, 1, opts);

    name = "C\000M\000P\000\000\000";
    args[0] = ONIG_TYPE_TAG | ONIG_TYPE_LONG;
    args[1] = ONIG_TYPE_STRING;
    args[2] = ONIG_TYPE_TAG | ONIG_TYPE_LONG;
    BC_P(name, cmp, 3, args);

#endif /* USE_CALLOUT */

  return ONIG_NORMAL;
}

Project: php-src
File: utf16_be.c

is_valid_mbc_string(const UChar* s, const UChar* end)
{
  return onigenc_length_check_is_valid_mbc_string(ONIG_ENCODING_UTF16_BE, s, end);
}

Project: php-src
File: utf32_be.c

utf32be_is_mbc_ambiguous(OnigCaseFoldType flag, const UChar** pp, const UChar* end)
{
  const UChar* p = *pp;

  (*pp) += 4;

  if (*(p+2) == 0 && *(p+1) == 0 && *p == 0) {
    int c, v;

    p += 3;
    if (*p == 0xdf && (flag & INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR) != 0) {
      return TRUE;
    }

    c = *p;
    v = ONIGENC_IS_UNICODE_ISO_8859_1_BIT_CTYPE(c,
                       (BIT_CTYPE_UPPER | BIT_CTYPE_LOWER));
    if ((v | BIT_CTYPE_LOWER) != 0) {
      /* 0xaa, 0xb5, 0xba are lower case letter, but can't convert. */
      if (c >= 0xaa && c <= 0xba)
        return FALSE;
      else
        return TRUE;
    }
    return (v != 0 ? TRUE : FALSE);
  }

  return FALSE;
}

Project: php-src
File: utf16_be.c

utf16be_mbc_to_code(const UChar* p, const UChar* end)
{
  OnigCodePoint code;

  if (UTF16_IS_SURROGATE_FIRST(*p)) {
    if (end - p < 4) return 0;
    code = ((((p[0] - 0xd8) << 2) + ((p[1] & 0xc0) >> 6) + 1) << 16)
         + ((((p[1] & 0x3f) << 2) + (p[2] - 0xdc)) << 8)
         + p[3];
  }
  else {
    if (end - p < 2) return 0;
    code = p[0] * 256 + p[1];
  }
  return code;
}

Project: php-src
File: utf32_be.c

utf32be_mbc_case_fold(OnigCaseFoldType flag,
		      const UChar** pp, const UChar* end, UChar* fold)
{
  const UChar* p = *pp;

  if (ONIGENC_IS_ASCII_CODE(*(p+3)) && *(p+2) == 0 && *(p+1) == 0 && *p == 0) {
    *fold++ = 0;
    *fold++ = 0;

#ifdef USE_UNICODE_CASE_FOLD_TURKISH_AZERI
    if ((flag & ONIGENC_CASE_FOLD_TURKISH_AZERI) != 0) {
      if (*(p+3) == 0x49) {
        *fold++ = 0x01;
        *fold   = 0x31;
        (*pp) += 4;
        return 4;
      }
    }
#endif

    *fold++ = 0;
    *fold   = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*(p+3));
    *pp += 4;
    return 4;
  }
  else
    return onigenc_unicode_mbc_case_fold(ONIG_ENCODING_UTF32_BE, flag, pp, end,
					 fold);
}

Project: php-src
File: utf32_be.c

utf32be_mbc_to_code(const UChar* p, const UChar* end ARG_UNUSED)
{
  if (end - p < 4) return 0;
  return (OnigCodePoint )(((p[0] * 256 + p[1]) * 256 + p[2]) * 256 + p[3]);
}

Project: php-src
File: utf16_le.c

utf16le_is_mbc_newline(const UChar* p, const UChar* end)
{
  if (p + 1 < end) {
    if (*p == 0x0a && *(p+1) == 0x00)
      return 1;
#ifdef USE_UNICODE_ALL_LINE_TERMINATORS
    if ((
#ifndef USE_CRNL_AS_LINE_TERMINATOR
         *p == 0x0d ||
#endif
         *p == 0x85) && *(p+1) == 0x00)
      return 1;

    if (*(p+1) == 0x20 && (*p == 0x29 || *p == 0x28))
      return 1;
#endif
  }
  return 0;
}

Project: php-src
File: utf32_le.c

is_valid_mbc_string(const UChar* s, const UChar* end)
{
  return onigenc_length_check_is_valid_mbc_string(ONIG_ENCODING_UTF32_LE, s, end);
}

Project: php-src
File: utf16_be.c

utf16be_is_mbc_newline(const UChar* p, const UChar* end)
{
  if (p + 1 < end) {
    if (*(p+1) == 0x0a && *p == 0x00)
      return 1;
#ifdef USE_UNICODE_ALL_LINE_TERMINATORS
    if ((
#ifndef USE_CRNL_AS_LINE_TERMINATOR
         *(p+1) == 0x0d ||
#endif
         *(p+1) == 0x85) && *p == 0x00)
      return 1;

    if (*p == 0x20 && (*(p+1) == 0x29 || *(p+1) == 0x28))
      return 1;
#endif
  }
  return 0;
}

Project: php-src
File: utf32_be.c

is_valid_mbc_string(const UChar* s, const UChar* end)
{
  return onigenc_length_check_is_valid_mbc_string(ONIG_ENCODING_UTF32_BE, s, end);
}

Project: php-src
File: utf16_le.c

utf16le_is_mbc_ambiguous(OnigCaseFoldType flag, const UChar** pp,
			 const UChar* end)
{
  const UChar* p = *pp;

  (*pp) += EncLen_UTF16[*(p+1)];

  if (*(p+1) == 0) {
    int c, v;

    if (*p == 0xdf && (flag & INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR) != 0) {
      return TRUE;
    }

    c = *p;
    v = ONIGENC_IS_UNICODE_ISO_8859_1_BIT_CTYPE(c,
                       (BIT_CTYPE_UPPER | BIT_CTYPE_LOWER));
    if ((v | BIT_CTYPE_LOWER) != 0) {
      /* 0xaa, 0xb5, 0xba are lower case letter, but can't convert. */
      if (c >= 0xaa && c <= 0xba)
        return FALSE;
      else
        return TRUE;
    }
    return (v != 0 ? TRUE : FALSE);
  }

  return FALSE;
}

Project: php-src
File: utf16_be.c

utf16be_is_mbc_ambiguous(OnigCaseFoldType flag, const UChar** pp, const UChar* end)
{
  const UChar* p = *pp;

  (*pp) += EncLen_UTF16[*p];

  if (*p == 0) {
    int c, v;

    p++;
    if (*p == 0xdf && (flag & INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR) != 0) {
      return TRUE;
    }

    c = *p;
    v = ONIGENC_IS_UNICODE_ISO_8859_1_BIT_CTYPE(c,
		(BIT_CTYPE_UPPER | BIT_CTYPE_LOWER));

    if ((v | BIT_CTYPE_LOWER) != 0) {
      /* 0xaa, 0xb5, 0xba are lower case letter, but can't convert. */
      if (c >= 0xaa && c <= 0xba)
        return FALSE;
      else
        return TRUE;
    }
    return (v != 0 ? TRUE : FALSE);
  }

  return FALSE;
}

Project: php-src
File: utf32_le.c

utf32le_is_mbc_ambiguous(OnigCaseFoldType flag, const UChar** pp, const UChar* end)
{
  const UChar* p = *pp;

  (*pp) += 4;

  if (*(p+1) == 0 && *(p+2) == 0 && *(p+3) == 0) {
    int c, v;

    if (*p == 0xdf && (flag & INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR) != 0) {
      return TRUE;
    }

    c = *p;
    v = ONIGENC_IS_UNICODE_ISO_8859_1_BIT_CTYPE(c,
                       (BIT_CTYPE_UPPER | BIT_CTYPE_LOWER));
    if ((v | BIT_CTYPE_LOWER) != 0) {
      /* 0xaa, 0xb5, 0xba are lower case letter, but can't convert. */
      if (c >= 0xaa && c <= 0xba)
        return FALSE;
      else
        return TRUE;
    }
    return (v != 0 ? TRUE : FALSE);
  }

  return FALSE;
}

Project: php-src
File: utf16_be.c

init(void)
{
#ifdef USE_CALLOUT

    int id;
    OnigEncoding enc;
    char* name;
    unsigned int args[4];
    OnigValue opts[4];

    enc = ONIG_ENCODING_UTF16_BE;

    name = "\000F\000A\000I\000L\000\000";            BC0_P(name, fail);
    name = "\000M\000I\000S\000M\000A\000T\000C\000H\000\000"; BC0_P(name, mismatch);

    name = "\000M\000A\000X\000\000";
    args[0] = ONIG_TYPE_TAG | ONIG_TYPE_LONG;
    args[1] = ONIG_TYPE_CHAR;
    opts[0].c = 'X';
    BC_B_O(name, max, 2, args, 1, opts);

    name = "\000E\000R\000R\000O\000R\000\000";
    args[0] = ONIG_TYPE_LONG; opts[0].l = ONIG_ABORT;
    BC_P_O(name, error, 1, args, 1, opts);

    name = "\000C\000O\000U\000N\000T\000\000";
    args[0] = ONIG_TYPE_CHAR; opts[0].c = '>';
    BC_B_O(name, count, 1, args, 1, opts);

    name = "\000T\000O\000T\000A\000L\000_\000C\000O\000U\000N\000T\000\000";
    args[0] = ONIG_TYPE_CHAR; opts[0].c = '>';
    BC_B_O(name, total_count, 1, args, 1, opts);

    name = "\000C\000M\000P\000\000";
    args[0] = ONIG_TYPE_TAG | ONIG_TYPE_LONG;
    args[1] = ONIG_TYPE_STRING;
    args[2] = ONIG_TYPE_TAG | ONIG_TYPE_LONG;
    BC_P(name, cmp, 3, args);

#endif /* USE_CALLOUT */

  return ONIG_NORMAL;
}

Project: php-src
File: utf16_be.c

utf16be_mbc_case_fold(OnigCaseFoldType flag,
		      const UChar** pp, const UChar* end, UChar* fold)
{
  const UChar* p = *pp;

  if (ONIGENC_IS_ASCII_CODE(*(p+1)) && *p == 0) {
    p++;
#ifdef USE_UNICODE_CASE_FOLD_TURKISH_AZERI
    if ((flag & ONIGENC_CASE_FOLD_TURKISH_AZERI) != 0) {
      if (*p == 0x49) {
        *fold++ = 0x01;
        *fold   = 0x31;
        (*pp) += 2;
        return 2;
      }
    }
#endif

    *fold++ = 0;
    *fold   = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*p);
    *pp += 2;
    return 2;
  }
  else
    return onigenc_unicode_mbc_case_fold(ONIG_ENCODING_UTF16_BE, flag,
					 pp, end, fold);
}

Project: php-src
File: utf16_le.c

is_valid_mbc_string(const UChar* p, const UChar* end)
{
  const UChar* end1 = end - 1;

  while (p < end1) {
    p += utf16le_mbc_enc_len(p);
  }

  if (p != end)
    return FALSE;
  else
    return TRUE;
}

Project: php-src
File: utf16_le.c

utf16le_mbc_case_fold(OnigCaseFoldType flag,
		      const UChar** pp, const UChar* end, UChar* fold)
{
  const UChar* p = *pp;

  if (ONIGENC_IS_ASCII_CODE(*p) && *(p+1) == 0) {
#ifdef USE_UNICODE_CASE_FOLD_TURKISH_AZERI
    if ((flag & ONIGENC_CASE_FOLD_TURKISH_AZERI) != 0) {
      if (*p == 0x49) {
        *fold++ = 0x31;
        *fold   = 0x01;
        (*pp) += 2;
        return 2;
      }
    }
#endif

    *fold++ = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*p);
    *fold   = 0;
    *pp += 2;
    return 2;
  }
  else
    return onigenc_unicode_mbc_case_fold(ONIG_ENCODING_UTF16_LE, flag, pp, end,
					 fold);
}

Project: php-src
File: utf16_le.c

utf16le_mbc_to_code(const UChar* p, const UChar* end)
{
  OnigCodePoint code;
  UChar c0 = *p;
  UChar c1 = *(p+1);

  if (UTF16_IS_SURROGATE_FIRST(c1)) {
    if (end - p < 4) return 0;
    code = ((((c1 - 0xd8) << 2) + ((c0  & 0xc0) >> 6) + 1) << 16)
         + ((((c0 & 0x3f) << 2) + (p[3] - 0xdc)) << 8)
         + p[2];
  }
  else {
    code = c1 * 256 + p[0];
  }
  return code;
}