security fixes

https://hhvm.com/blog/2021/02/25/security-update.html

String preg_quote(const String& str,
                  const String& delimiter /* = null_string */) {
  const char* in_str = str.data();
  const char* in_str_end = in_str + str.size();

  /* Nothing to do if we got an empty string */
  if (in_str == in_str_end) {
    return str;
  }

  char delim_char = 0;      /* Delimiter character to be quoted */
  bool quote_delim = false; /* Whether to quote additional delim char */
  if (!delimiter.empty()) {
    delim_char = delimiter.charAt(0);
    quote_delim = true;
  }

  /* Allocate enough memory so that even if each character
     is quoted, we won't run out of room */
  String ret(4 * str.size() + 1, ReserveString);
  char* out_str = ret.mutableData();

  /* Go through the string and quote necessary characters */
  const char* p;
  char* q;
  for (p = in_str, q = out_str; p != in_str_end; p++) {
    char c = *p;
    switch (c) {
    case '.': case '\\': case '+': case '*': case '?':
    case '[': case '^':  case ']': case '$': case '(':
    case ')': case '{':  case '}': case '=': case '!':
    case '>': case '<':  case '|': case ':': case '-':
    case '#':
      *q++ = '\\';
      *q++ = c;
      break;

    case '\0':
      *q++ = '\\';
      *q++ = '0';
      *q++ = '0';
      *q++ = '0';
      break;

    default:
      if (quote_delim && c == delim_char)
        *q++ = '\\';
      *q++ = c;
      break;
    }
  }
  *q = '\0';

  return ret.setSize(q - out_str);
}

In-memory file operations (ie: using fopen on a data URI) did not properly restrict negative seeking, allowing for the reading of memory prior to the in-memory buffer. This issue affects HHVM versions prior to 4.56.3, all versions between 4.57.0 and 4.80.1, all versions between 4.81.0 and 4.93.1, and versions 4.94.0, 4.95.0, 4.96.0, 4.97.0, 4.98.0.


String preg_quote(const String& str,
                  const String& delimiter /* = null_string */) {
  const char* in_str = str.data();
  const char* in_str_end = in_str + str.size();

  /* Nothing to do if we got an empty string */
  if (in_str == in_str_end) {
    return str;
  }

  char delim_char = 0;      /* Delimiter character to be quoted */
  bool quote_delim = false; /* Whether to quote additional delim char */
  if (!delimiter.empty()) {
    delim_char = delimiter.charAt(0);
    quote_delim = true;
  }

  /* Allocate enough memory so that even if each character
     is quoted, we won't run out of room */
  static_assert(
    (StringData::MaxSize * 4 + 1) < std::numeric_limits<int64_t>::max()
  );
  String ret(4 * str.size() + 1, ReserveString);
  char* out_str = ret.mutableData();

  /* Go through the string and quote necessary characters */
  const char* p;
  char* q;
  for (p = in_str, q = out_str; p != in_str_end; p++) {
    char c = *p;
    switch (c) {
    case '.': case '\\': case '+': case '*': case '?':
    case '[': case '^':  case ']': case '$': case '(':
    case ')': case '{':  case '}': case '=': case '!':
    case '>': case '<':  case '|': case ':': case '-':
    case '#':
      *q++ = '\\';
      *q++ = c;
      break;

    case '\0':
      *q++ = '\\';
      *q++ = '0';
      *q++ = '0';
      *q++ = '0';
      break;

    default:
      if (quote_delim && c == delim_char)
        *q++ = '\\';
      *q++ = c;
      break;
    }
  }
  *q = '\0';

  return ret.setSize(q - out_str);
}


static int xbuf_format_converter(char **outbuf, const char *fmt, va_list ap)
{
  register char *s = nullptr;
  char *q;
  int s_len;

  register int min_width = 0;
  int precision = 0;
  enum {
    LEFT, RIGHT
  } adjust;
  char pad_char;
  char prefix_char;

  double fp_num;
  wide_int i_num = (wide_int) 0;
  u_wide_int ui_num;

  char num_buf[NUM_BUF_SIZE];
  char char_buf[2];      /* for printing %% and %<unknown> */

#ifdef HAVE_LOCALE_H
  struct lconv *lconv = nullptr;
#endif

  /*
   * Flag variables
   */
  length_modifier_e modifier;
  boolean_e alternate_form;
  boolean_e print_sign;
  boolean_e print_blank;
  boolean_e adjust_precision;
  boolean_e adjust_width;
  int is_negative;

  int size = 240;
  char *result = (char *)malloc(size);
  int outpos = 0;

  while (*fmt) {
    if (*fmt != '%') {
      appendchar(&result, &outpos, &size, *fmt);
    } else {
      /*
       * Default variable settings
       */
      adjust = RIGHT;
      alternate_form = print_sign = print_blank = NO;
      pad_char = ' ';
      prefix_char = NUL;

      fmt++;

      /*
       * Try to avoid checking for flags, width or precision
       */
      if (isascii((int)*fmt) && !islower((int)*fmt)) {
        /*
         * Recognize flags: -, #, BLANK, +
         */
        for (;; fmt++) {
          if (*fmt == '-')
            adjust = LEFT;
          else if (*fmt == '+')
            print_sign = YES;
          else if (*fmt == '#')
            alternate_form = YES;
          else if (*fmt == ' ')
            print_blank = YES;
          else if (*fmt == '0')
            pad_char = '0';
          else
            break;
        }

        /*
         * Check if a width was specified
         */
        if (isdigit((int)*fmt)) {
          STR_TO_DEC(fmt, min_width);
          adjust_width = YES;
        } else if (*fmt == '*') {
          min_width = va_arg(ap, int);
          fmt++;
          adjust_width = YES;
          if (min_width < 0) {
            adjust = LEFT;
            min_width = -min_width;
          }
        } else
          adjust_width = NO;

        /*
         * Check if a precision was specified
         *
         * XXX: an unreasonable amount of precision may be specified
         * resulting in overflow of num_buf. Currently we
         * ignore this possibility.
         */
        if (*fmt == '.') {
          adjust_precision = YES;
          fmt++;
          if (isdigit((int)*fmt)) {
            STR_TO_DEC(fmt, precision);
          } else if (*fmt == '*') {
            precision = va_arg(ap, int);
            fmt++;
            if (precision < 0)
              precision = 0;
          } else
            precision = 0;
        } else
          adjust_precision = NO;
      } else
        adjust_precision = adjust_width = NO;

      /*
       * Modifier check
       */
      switch (*fmt) {
        case 'L':
          fmt++;
          modifier = LM_LONG_DOUBLE;
          break;
        case 'I':
          fmt++;
#if SIZEOF_LONG_LONG
          if (*fmt == '6' && *(fmt+1) == '4') {
            fmt += 2;
            modifier = LM_LONG_LONG;
          } else
#endif
            if (*fmt == '3' && *(fmt+1) == '2') {
              fmt += 2;
              modifier = LM_LONG;
            } else {
#ifdef _WIN64
              modifier = LM_LONG_LONG;
#else
              modifier = LM_LONG;
#endif
            }
          break;
        case 'l':
          fmt++;
#if SIZEOF_LONG_LONG
          if (*fmt == 'l') {
            fmt++;
            modifier = LM_LONG_LONG;
          } else
#endif
            modifier = LM_LONG;
          break;
        case 'z':
          fmt++;
          modifier = LM_SIZE_T;
          break;
        case 'j':
          fmt++;
#if SIZEOF_INTMAX_T
          modifier = LM_INTMAX_T;
#else
          modifier = LM_SIZE_T;
#endif
          break;
        case 't':
          fmt++;
#if SIZEOF_PTRDIFF_T
          modifier = LM_PTRDIFF_T;
#else
          modifier = LM_SIZE_T;
#endif
          break;
        case 'h':
          fmt++;
          if (*fmt == 'h') {
            fmt++;
          }
          /* these are promoted to int, so no break */
        default:
          modifier = LM_STD;
          break;
      }

      /*
       * Argument extraction and printing.
       * First we determine the argument type.
       * Then, we convert the argument to a string.
       * On exit from the switch, s points to the string that
       * must be printed, s_len has the length of the string
       * The precision requirements, if any, are reflected in s_len.
       *
       * NOTE: pad_char may be set to '0' because of the 0 flag.
       *   It is reset to ' ' by non-numeric formats
       */
      switch (*fmt) {
        case 'u':
          switch(modifier) {
            default:
              i_num = (wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              i_num = (wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              i_num = (wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              i_num = (wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              i_num = (wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              i_num = (wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          /*
           * The rest also applies to other integer formats, so fall
           * into that case.
           */
        case 'd':
        case 'i':
          /*
           * Get the arg if we haven't already.
           */
          if ((*fmt) != 'u') {
            switch(modifier) {
              default:
                i_num = (wide_int) va_arg(ap, int);
                break;
              case LM_LONG_DOUBLE:
                goto fmt_error;
              case LM_LONG:
                i_num = (wide_int) va_arg(ap, long int);
                break;
              case LM_SIZE_T:
#if SIZEOF_SSIZE_T
                i_num = (wide_int) va_arg(ap, ssize_t);
#else
                i_num = (wide_int) va_arg(ap, size_t);
#endif
                break;
#if SIZEOF_LONG_LONG
              case LM_LONG_LONG:
                i_num = (wide_int) va_arg(ap, wide_int);
                break;
#endif
#if SIZEOF_INTMAX_T
              case LM_INTMAX_T:
                i_num = (wide_int) va_arg(ap, intmax_t);
                break;
#endif
#if SIZEOF_PTRDIFF_T
              case LM_PTRDIFF_T:
                i_num = (wide_int) va_arg(ap, ptrdiff_t);
                break;
#endif
            }
          }
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);

          if (*fmt != 'u') {
            if (is_negative)
              prefix_char = '-';
            else if (print_sign)
              prefix_char = '+';
            else if (print_blank)
              prefix_char = ' ';
          }
          break;

        case 'o':
          switch(modifier) {
            default:
              ui_num = (u_wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              ui_num = (u_wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              ui_num = (u_wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              ui_num = (u_wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              ui_num = (u_wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          s = ap_php_conv_p2(ui_num, 3, *fmt,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);
          if (alternate_form && *s != '0') {
            *--s = '0';
            s_len++;
          }
          break;

        case 'x':
        case 'X':
          switch(modifier) {
            default:
              ui_num = (u_wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              ui_num = (u_wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              ui_num = (u_wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              ui_num = (u_wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              ui_num = (u_wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          s = ap_php_conv_p2(ui_num, 4, *fmt,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);
          if (alternate_form && i_num != 0) {
            *--s = *fmt;  /* 'x' or 'X' */
            *--s = '0';
            s_len += 2;
          }
          break;

        case 's':
        case 'v':
          s = va_arg(ap, char *);
          if (s != nullptr) {
            s_len = strlen(s);
            if (adjust_precision && precision < s_len)
              s_len = precision;
          } else {
            s = const_cast<char*>(s_null);
            s_len = S_NULL_LEN;
          }
          pad_char = ' ';
          break;

        case 'f':
        case 'F':
        case 'e':
        case 'E':
          switch(modifier) {
            case LM_LONG_DOUBLE:
              fp_num = (double) va_arg(ap, long double);
              break;
            case LM_STD:
              fp_num = va_arg(ap, double);
              break;
            default:
              goto fmt_error;
          }

          if (std::isnan(fp_num)) {
            s = const_cast<char*>("nan");
            s_len = 3;
          } else if (std::isinf(fp_num)) {
            s = const_cast<char*>("inf");
            s_len = 3;
          } else {
#ifdef HAVE_LOCALE_H
            if (!lconv) {
              lconv = localeconv();
            }
#endif
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
             (adjust_precision == NO) ? FLOAT_DIGITS : precision,
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
                  &is_negative, &num_buf[1], &s_len);
            if (is_negative)
              prefix_char = '-';
            else if (print_sign)
              prefix_char = '+';
            else if (print_blank)
              prefix_char = ' ';
          }
          break;

        case 'g':
        case 'k':
        case 'G':
        case 'H':
          switch(modifier) {
            case LM_LONG_DOUBLE:
              fp_num = (double) va_arg(ap, long double);
              break;
            case LM_STD:
              fp_num = va_arg(ap, double);
              break;
            default:
              goto fmt_error;
          }

          if (std::isnan(fp_num)) {
             s = const_cast<char*>("NAN");
             s_len = 3;
             break;
           } else if (std::isinf(fp_num)) {
             if (fp_num > 0) {
               s = const_cast<char*>("INF");
               s_len = 3;
             } else {
               s = const_cast<char*>("-INF");
               s_len = 4;
             }
             break;
           }

          if (adjust_precision == NO)
            precision = FLOAT_DIGITS;
          else if (precision == 0)
            precision = 1;
          /*
           * * We use &num_buf[ 1 ], so that we have room for the sign
           */
#ifdef HAVE_LOCALE_H
          if (!lconv) {
            lconv = localeconv();
          }
#endif
          s = php_gcvt(fp_num, precision,
                       (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT,
                       (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
          if (*s == '-')
            prefix_char = *s++;
          else if (print_sign)
            prefix_char = '+';
          else if (print_blank)
            prefix_char = ' ';

          s_len = strlen(s);

          if (alternate_form && (q = strchr(s, '.')) == nullptr)
            s[s_len++] = '.';
          break;

        case 'c':
          char_buf[0] = (char) (va_arg(ap, int));
          s = &char_buf[0];
          s_len = 1;
          pad_char = ' ';
          break;

        case '%':
          char_buf[0] = '%';
          s = &char_buf[0];
          s_len = 1;
          pad_char = ' ';
          break;

        case 'n':
          *(va_arg(ap, int *)) = outpos;
          goto skip_output;

          /*
           * Always extract the argument as a "char *" pointer. We
           * should be using "void *" but there are still machines
           * that don't understand it.
           * If the pointer size is equal to the size of an unsigned
           * integer we convert the pointer to a hex number, otherwise
           * we print "%p" to indicate that we don't handle "%p".
           */
        case 'p':
          if (sizeof(char *) <= sizeof(u_wide_int)) {
            ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
            s = ap_php_conv_p2(ui_num, 4, 'x',
                &num_buf[NUM_BUF_SIZE], &s_len);
            if (ui_num != 0) {
              *--s = 'x';
              *--s = '0';
              s_len += 2;
            }
          } else {
            s = const_cast<char*>("%p");
            s_len = 2;
          }
          pad_char = ' ';
          break;

        case NUL:
          /*
           * The last character of the format string was %.
           * We ignore it.
           */
          continue;

fmt_error:
        throw Exception("Illegal length modifier specified '%c'", *fmt);

          /*
           * The default case is for unrecognized %'s.
           * We print %<char> to help the user identify what
           * option is not understood.
           * This is also useful in case the user wants to pass
           * the output of format_converter to another function
           * that understands some other %<char> (like syslog).
           * Note that we can't point s inside fmt because the
           * unknown <char> could be preceded by width etc.
           */
        default:
          char_buf[0] = '%';
          char_buf[1] = *fmt;
          s = char_buf;
          s_len = 2;
          pad_char = ' ';
          break;
      }

      if (prefix_char != NUL) {
        *--s = prefix_char;
        s_len++;
      }
      if (adjust_width && adjust == RIGHT && min_width > s_len) {
        if (pad_char == '0' && prefix_char != NUL) {
          appendchar(&result, &outpos, &size, *s);
          s++;
          s_len--;
          min_width--;
        }
        for (int i = 0; i < min_width - s_len; i++) {
          appendchar(&result, &outpos, &size, pad_char);
        }
      }
      /*
       * Print the (for now) non-null terminated string s.
       */
      appendsimplestring(&result, &outpos, &size, s, s_len);

      if (adjust_width && adjust == LEFT && min_width > s_len) {
        for (int i = 0; i < min_width - s_len; i++) {
          appendchar(&result, &outpos, &size, pad_char);
        }
      }
    }
skip_output:
    fmt++;
  }
  /*
   * Add the terminating null here since it wasn't added incrementally above
   * once the whole string has been composed.
   */
  result[outpos] = NUL;
  *outbuf = result;
  return outpos;
}


static int xbuf_format_converter(char **outbuf, const char *fmt, va_list ap)
{
  register char *s = nullptr;
  char *q;
  int s_len;

  register int min_width = 0;
  int precision = 0;
  enum {
    LEFT, RIGHT
  } adjust;
  char pad_char;
  char prefix_char;

  double fp_num;
  wide_int i_num = (wide_int) 0;
  u_wide_int ui_num;

  char num_buf[NUM_BUF_SIZE];
  char char_buf[2];      /* for printing %% and %<unknown> */

#ifdef HAVE_LOCALE_H
  struct lconv *lconv = nullptr;
#endif

  /*
   * Flag variables
   */
  length_modifier_e modifier;
  boolean_e alternate_form;
  boolean_e print_sign;
  boolean_e print_blank;
  boolean_e adjust_precision;
  boolean_e adjust_width;
  int is_negative;

  int size = 240;
  char *result = (char *)malloc(size);
  int outpos = 0;

  while (*fmt) {
    if (*fmt != '%') {
      appendchar(&result, &outpos, &size, *fmt);
    } else {
      /*
       * Default variable settings
       */
      adjust = RIGHT;
      alternate_form = print_sign = print_blank = NO;
      pad_char = ' ';
      prefix_char = NUL;

      fmt++;

      /*
       * Try to avoid checking for flags, width or precision
       */
      if (isascii((int)*fmt) && !islower((int)*fmt)) {
        /*
         * Recognize flags: -, #, BLANK, +
         */
        for (;; fmt++) {
          if (*fmt == '-')
            adjust = LEFT;
          else if (*fmt == '+')
            print_sign = YES;
          else if (*fmt == '#')
            alternate_form = YES;
          else if (*fmt == ' ')
            print_blank = YES;
          else if (*fmt == '0')
            pad_char = '0';
          else
            break;
        }

        /*
         * Check if a width was specified
         */
        if (isdigit((int)*fmt)) {
          STR_TO_DEC(fmt, min_width);
          adjust_width = YES;
        } else if (*fmt == '*') {
          min_width = va_arg(ap, int);
          fmt++;
          adjust_width = YES;
          if (min_width < 0) {
            adjust = LEFT;
            min_width = -min_width;
          }
        } else
          adjust_width = NO;

        /*
         * Check if a precision was specified
         *
         * XXX: an unreasonable amount of precision may be specified
         * resulting in overflow of num_buf. Currently we
         * ignore this possibility.
         */
        if (*fmt == '.') {
          adjust_precision = YES;
          fmt++;
          if (isdigit((int)*fmt)) {
            STR_TO_DEC(fmt, precision);
          } else if (*fmt == '*') {
            precision = va_arg(ap, int);
            fmt++;
            if (precision < 0)
              precision = 0;
          } else
            precision = 0;
        } else
          adjust_precision = NO;
      } else
        adjust_precision = adjust_width = NO;

      /*
       * Modifier check
       */
      switch (*fmt) {
        case 'L':
          fmt++;
          modifier = LM_LONG_DOUBLE;
          break;
        case 'I':
          fmt++;
#if SIZEOF_LONG_LONG
          if (*fmt == '6' && *(fmt+1) == '4') {
            fmt += 2;
            modifier = LM_LONG_LONG;
          } else
#endif
            if (*fmt == '3' && *(fmt+1) == '2') {
              fmt += 2;
              modifier = LM_LONG;
            } else {
#ifdef _WIN64
              modifier = LM_LONG_LONG;
#else
              modifier = LM_LONG;
#endif
            }
          break;
        case 'l':
          fmt++;
#if SIZEOF_LONG_LONG
          if (*fmt == 'l') {
            fmt++;
            modifier = LM_LONG_LONG;
          } else
#endif
            modifier = LM_LONG;
          break;
        case 'z':
          fmt++;
          modifier = LM_SIZE_T;
          break;
        case 'j':
          fmt++;
#if SIZEOF_INTMAX_T
          modifier = LM_INTMAX_T;
#else
          modifier = LM_SIZE_T;
#endif
          break;
        case 't':
          fmt++;
#if SIZEOF_PTRDIFF_T
          modifier = LM_PTRDIFF_T;
#else
          modifier = LM_SIZE_T;
#endif
          break;
        case 'h':
          fmt++;
          if (*fmt == 'h') {
            fmt++;
          }
          /* these are promoted to int, so no break */
        default:
          modifier = LM_STD;
          break;
      }

      /*
       * Argument extraction and printing.
       * First we determine the argument type.
       * Then, we convert the argument to a string.
       * On exit from the switch, s points to the string that
       * must be printed, s_len has the length of the string
       * The precision requirements, if any, are reflected in s_len.
       *
       * NOTE: pad_char may be set to '0' because of the 0 flag.
       *   It is reset to ' ' by non-numeric formats
       */
      switch (*fmt) {
        case 'u':
          switch(modifier) {
            default:
              i_num = (wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              i_num = (wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              i_num = (wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              i_num = (wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              i_num = (wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              i_num = (wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          /*
           * The rest also applies to other integer formats, so fall
           * into that case.
           */
        case 'd':
        case 'i':
          /*
           * Get the arg if we haven't already.
           */
          if ((*fmt) != 'u') {
            switch(modifier) {
              default:
                i_num = (wide_int) va_arg(ap, int);
                break;
              case LM_LONG_DOUBLE:
                goto fmt_error;
              case LM_LONG:
                i_num = (wide_int) va_arg(ap, long int);
                break;
              case LM_SIZE_T:
#if SIZEOF_SSIZE_T
                i_num = (wide_int) va_arg(ap, ssize_t);
#else
                i_num = (wide_int) va_arg(ap, size_t);
#endif
                break;
#if SIZEOF_LONG_LONG
              case LM_LONG_LONG:
                i_num = (wide_int) va_arg(ap, wide_int);
                break;
#endif
#if SIZEOF_INTMAX_T
              case LM_INTMAX_T:
                i_num = (wide_int) va_arg(ap, intmax_t);
                break;
#endif
#if SIZEOF_PTRDIFF_T
              case LM_PTRDIFF_T:
                i_num = (wide_int) va_arg(ap, ptrdiff_t);
                break;
#endif
            }
          }
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);

          if (*fmt != 'u') {
            if (is_negative)
              prefix_char = '-';
            else if (print_sign)
              prefix_char = '+';
            else if (print_blank)
              prefix_char = ' ';
          }
          break;

        case 'o':
          switch(modifier) {
            default:
              ui_num = (u_wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              ui_num = (u_wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              ui_num = (u_wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              ui_num = (u_wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              ui_num = (u_wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          s = ap_php_conv_p2(ui_num, 3, *fmt,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);
          if (alternate_form && *s != '0') {
            *--s = '0';
            s_len++;
          }
          break;

        case 'x':
        case 'X':
          switch(modifier) {
            default:
              ui_num = (u_wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              ui_num = (u_wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              ui_num = (u_wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              ui_num = (u_wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              ui_num = (u_wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          s = ap_php_conv_p2(ui_num, 4, *fmt,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);
          if (alternate_form && i_num != 0) {
            *--s = *fmt;  /* 'x' or 'X' */
            *--s = '0';
            s_len += 2;
          }
          break;

        case 's':
        case 'v':
          s = va_arg(ap, char *);
          if (s != nullptr) {
            s_len = strlen(s);
            if (adjust_precision && precision < s_len)
              s_len = precision;
          } else {
            s = const_cast<char*>(s_null);
            s_len = S_NULL_LEN;
          }
          pad_char = ' ';
          break;

        case 'f':
        case 'F':
        case 'e':
        case 'E':
          switch(modifier) {
            case LM_LONG_DOUBLE:
              fp_num = (double) va_arg(ap, long double);
              break;
            case LM_STD:
              fp_num = va_arg(ap, double);
              break;
            default:
              goto fmt_error;
          }

          if (std::isnan(fp_num)) {
            s = const_cast<char*>("nan");
            s_len = 3;
          } else if (std::isinf(fp_num)) {
            s = const_cast<char*>("inf");
            s_len = 3;
          } else {
#ifdef HAVE_LOCALE_H
            if (!lconv) {
              lconv = localeconv();
            }
#endif
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
             (adjust_precision == NO) ? FLOAT_DIGITS : precision,
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
                  &is_negative, &num_buf[1], &s_len);
            if (is_negative)
              prefix_char = '-';
            else if (print_sign)
              prefix_char = '+';
            else if (print_blank)
              prefix_char = ' ';
          }
          break;

        case 'g':
        case 'k':
        case 'G':
        case 'H':
          switch(modifier) {
            case LM_LONG_DOUBLE:
              fp_num = (double) va_arg(ap, long double);
              break;
            case LM_STD:
              fp_num = va_arg(ap, double);
              break;
            default:
              goto fmt_error;
          }

          if (std::isnan(fp_num)) {
             s = const_cast<char*>("NAN");
             s_len = 3;
             break;
           } else if (std::isinf(fp_num)) {
             if (fp_num > 0) {
               s = const_cast<char*>("INF");
               s_len = 3;
             } else {
               s = const_cast<char*>("-INF");
               s_len = 4;
             }
             break;
           }

          if (adjust_precision == NO)
            precision = FLOAT_DIGITS;
          else if (precision == 0)
            precision = 1;
          /*
           * * We use &num_buf[ 1 ], so that we have room for the sign
           */
#ifdef HAVE_LOCALE_H
          if (!lconv) {
            lconv = localeconv();
          }
#endif
          s = php_gcvt(fp_num, precision,
                       (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT,
                       (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
          if (*s == '-')
            prefix_char = *s++;
          else if (print_sign)
            prefix_char = '+';
          else if (print_blank)
            prefix_char = ' ';

          s_len = strlen(s);

          if (alternate_form && (q = strchr(s, '.')) == nullptr)
            s[s_len++] = '.';
          break;

        case 'c':
          char_buf[0] = (char) (va_arg(ap, int));
          s = &char_buf[0];
          s_len = 1;
          pad_char = ' ';
          break;

        case '%':
          char_buf[0] = '%';
          s = &char_buf[0];
          s_len = 1;
          pad_char = ' ';
          break;

        case 'n':
          *(va_arg(ap, int *)) = outpos;
          goto skip_output;

          /*
           * Always extract the argument as a "char *" pointer. We
           * should be using "void *" but there are still machines
           * that don't understand it.
           * If the pointer size is equal to the size of an unsigned
           * integer we convert the pointer to a hex number, otherwise
           * we print "%p" to indicate that we don't handle "%p".
           */
        case 'p':
          if (sizeof(char *) <= sizeof(u_wide_int)) {
            ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
            s = ap_php_conv_p2(ui_num, 4, 'x',
                &num_buf[NUM_BUF_SIZE], &s_len);
            if (ui_num != 0) {
              *--s = 'x';
              *--s = '0';
              s_len += 2;
            }
          } else {
            s = const_cast<char*>("%p");
            s_len = 2;
          }
          pad_char = ' ';
          break;

        case NUL:
          /*
           * The last character of the format string was %.
           * We ignore it.
           */
          continue;

fmt_error:
        throw Exception("Illegal length modifier specified '%c'", *fmt);

          /*
           * The default case is for unrecognized %'s.
           * We print %<char> to help the user identify what
           * option is not understood.
           * This is also useful in case the user wants to pass
           * the output of format_converter to another function
           * that understands some other %<char> (like syslog).
           * Note that we can't point s inside fmt because the
           * unknown <char> could be preceded by width etc.
           */
        default:
          char_buf[0] = '%';
          char_buf[1] = *fmt;
          s = char_buf;
          s_len = 2;
          pad_char = ' ';
          break;
      }

      if (prefix_char != NUL) {
        *--s = prefix_char;
        s_len++;
      }
      if (adjust_width && adjust == RIGHT && min_width > s_len) {
        if (pad_char == '0' && prefix_char != NUL) {
          appendchar(&result, &outpos, &size, *s);
          s++;
          s_len--;
          min_width--;
        }
        for (int i = 0; i < min_width - s_len; i++) {
          appendchar(&result, &outpos, &size, pad_char);
        }
      }
      /*
       * Print the (for now) non-null terminated string s.
       */
      appendsimplestring(&result, &outpos, &size, s, s_len);

      if (adjust_width && adjust == LEFT && min_width > s_len) {
        for (int i = 0; i < min_width - s_len; i++) {
          appendchar(&result, &outpos, &size, pad_char);
        }
      }
    }
skip_output:
    fmt++;
  }
  /*
   * Add the terminating null here since it wasn't added incrementally above
   * once the whole string has been composed.
   */
  appendchar(&result, &outpos, &size, NUL);
  *outbuf = result;
  return outpos - 1;
}


[Playlist] dont use istream directly to a tinyxml structure

Turn istream into a std::string to handle large buffers (#20305)

bool CPlayListASX::LoadData(std::istream& stream)
{
  CLog::Log(LOGINFO, "Parsing ASX");

  if(stream.peek() == '[')
  {
    return LoadAsxIniInfo(stream);
  }
  else
  {
    CXBMCTinyXML xmlDoc;
    stream >> xmlDoc;

    if (xmlDoc.Error())
    {
      CLog::Log(LOGERROR, "Unable to parse ASX info Error: {}", xmlDoc.ErrorDesc());
      return false;
    }

    TiXmlElement *pRootElement = xmlDoc.RootElement();

    // lowercase every element
    TiXmlNode *pNode = pRootElement;
    TiXmlNode *pChild = NULL;
    std::string value;
    value = pNode->Value();
    StringUtils::ToLower(value);
    pNode->SetValue(value);
    while(pNode)
    {
      pChild = pNode->IterateChildren(pChild);
      if(pChild)
      {
        if (pChild->Type() == TiXmlNode::TINYXML_ELEMENT)
        {
          value = pChild->Value();
          StringUtils::ToLower(value);
          pChild->SetValue(value);

          TiXmlAttribute* pAttr = pChild->ToElement()->FirstAttribute();
          while(pAttr)
          {
            value = pAttr->Name();
            StringUtils::ToLower(value);
            pAttr->SetName(value);
            pAttr = pAttr->Next();
          }
        }

        pNode = pChild;
        pChild = NULL;
        continue;
      }

      pChild = pNode;
      pNode = pNode->Parent();
    }
    std::string roottitle;
    TiXmlElement *pElement = pRootElement->FirstChildElement();
    while (pElement)
    {
      value = pElement->Value();
      if (value == "title" && !pElement->NoChildren())
      {
        roottitle = pElement->FirstChild()->ValueStr();
      }
      else if (value == "entry")
      {
        std::string title(roottitle);

        TiXmlElement *pRef = pElement->FirstChildElement("ref");
        TiXmlElement *pTitle = pElement->FirstChildElement("title");

        if(pTitle && !pTitle->NoChildren())
          title = pTitle->FirstChild()->ValueStr();

        while (pRef)
        { // multiple references may appear for one entry
          // duration may exist on this level too
          value = XMLUtils::GetAttribute(pRef, "href");
          if (!value.empty())
          {
            if(title.empty())
              title = value;

            CLog::Log(LOGINFO, "Adding element {}, {}", title, value);
            CFileItemPtr newItem(new CFileItem(title));
            newItem->SetPath(value);
            Add(newItem);
          }
          pRef = pRef->NextSiblingElement("ref");
        }
      }
      else if (value == "entryref")
      {
        value = XMLUtils::GetAttribute(pElement, "href");
        if (!value.empty())
        { // found an entryref, let's try loading that url
          std::unique_ptr<CPlayList> playlist(CPlayListFactory::Create(value));
          if (nullptr != playlist)
            if (playlist->Load(value))
              Add(*playlist);
        }
      }
      pElement = pElement->NextSiblingElement();
    }
  }

  return true;
}

Buffer overflow vulnerability in Kodi xbmc up to 19.0, allows attackers to cause a denial of service due to improper length of values passed to istream.


bool CPlayListASX::LoadData(std::istream& stream)
{
  CLog::Log(LOGINFO, "Parsing ASX");

  if(stream.peek() == '[')
  {
    return LoadAsxIniInfo(stream);
  }
  else
  {
    std::string asxstream(std::istreambuf_iterator<char>(stream), {});
    CXBMCTinyXML xmlDoc;
    xmlDoc.Parse(asxstream, TIXML_DEFAULT_ENCODING);

    if (xmlDoc.Error())
    {
      CLog::Log(LOGERROR, "Unable to parse ASX info Error: {}", xmlDoc.ErrorDesc());
      return false;
    }

    TiXmlElement *pRootElement = xmlDoc.RootElement();

    if (!pRootElement)
      return false;

    // lowercase every element
    TiXmlNode *pNode = pRootElement;
    TiXmlNode *pChild = NULL;
    std::string value;
    value = pNode->Value();
    StringUtils::ToLower(value);
    pNode->SetValue(value);
    while(pNode)
    {
      pChild = pNode->IterateChildren(pChild);
      if(pChild)
      {
        if (pChild->Type() == TiXmlNode::TINYXML_ELEMENT)
        {
          value = pChild->Value();
          StringUtils::ToLower(value);
          pChild->SetValue(value);

          TiXmlAttribute* pAttr = pChild->ToElement()->FirstAttribute();
          while(pAttr)
          {
            value = pAttr->Name();
            StringUtils::ToLower(value);
            pAttr->SetName(value);
            pAttr = pAttr->Next();
          }
        }

        pNode = pChild;
        pChild = NULL;
        continue;
      }

      pChild = pNode;
      pNode = pNode->Parent();
    }
    std::string roottitle;
    TiXmlElement *pElement = pRootElement->FirstChildElement();
    while (pElement)
    {
      value = pElement->Value();
      if (value == "title" && !pElement->NoChildren())
      {
        roottitle = pElement->FirstChild()->ValueStr();
      }
      else if (value == "entry")
      {
        std::string title(roottitle);

        TiXmlElement *pRef = pElement->FirstChildElement("ref");
        TiXmlElement *pTitle = pElement->FirstChildElement("title");

        if(pTitle && !pTitle->NoChildren())
          title = pTitle->FirstChild()->ValueStr();

        while (pRef)
        { // multiple references may appear for one entry
          // duration may exist on this level too
          value = XMLUtils::GetAttribute(pRef, "href");
          if (!value.empty())
          {
            if(title.empty())
              title = value;

            CLog::Log(LOGINFO, "Adding element {}, {}", title, value);
            CFileItemPtr newItem(new CFileItem(title));
            newItem->SetPath(value);
            Add(newItem);
          }
          pRef = pRef->NextSiblingElement("ref");
        }
      }
      else if (value == "entryref")
      {
        value = XMLUtils::GetAttribute(pElement, "href");
        if (!value.empty())
        { // found an entryref, let's try loading that url
          std::unique_ptr<CPlayList> playlist(CPlayListFactory::Create(value));
          if (nullptr != playlist)
            if (playlist->Load(value))
              Add(*playlist);
        }
      }
      pElement = pElement->NextSiblingElement();
    }
  }

  return true;
}


[lite] add validation check for sparse fully connected

PiperOrigin-RevId: 417629354
Change-Id: If96171c4bd4f5fdb01d6368d6deab19d1c9beca7

TfLiteStatus EvalFloat(TfLiteContext* context, TfLiteNode* node,
                       TfLiteFullyConnectedParams* params, OpData* data,
                       const TfLiteTensor* input, const TfLiteTensor* filter,
                       const TfLiteTensor* bias, TfLiteTensor* output) {
  float output_activation_min, output_activation_max;
  CalculateActivationRange(params->activation, &output_activation_min,
                           &output_activation_max);
  if (kernel_type == kReference) {
    FullyConnectedParams op_params;
    op_params.float_activation_min = output_activation_min;
    op_params.float_activation_max = output_activation_max;
    if (filter->sparsity != nullptr) {
      const auto& sparsity = *filter->sparsity;
      reference_ops::FullyConnectedSparseWeight(
          sparsity, op_params, GetTensorShape(input),
          GetTensorData<float>(input), GetTensorShape(filter),
          GetTensorData<float>(filter), GetTensorShape(bias),
          GetTensorData<float>(bias), GetTensorShape(output),
          GetTensorData<float>(output));
    } else {
      reference_ops::FullyConnected(
          op_params, GetTensorShape(input), GetTensorData<float>(input),
          GetTensorShape(filter), GetTensorData<float>(filter),
          GetTensorShape(bias), GetTensorData<float>(bias),
          GetTensorShape(output), GetTensorData<float>(output));
    }
  } else if (kernel_type == kLegacyPie) {
    return EvalPie(context, node, params, data, input, filter, bias, output);
  } else {
    FullyConnectedParams op_params;
    op_params.float_activation_min = output_activation_min;
    op_params.float_activation_max = output_activation_max;
    if (filter->sparsity != nullptr) {
      const auto& sparsity = *filter->sparsity;
      if (!SupportedSparsityFormat(sparsity)) {
        TF_LITE_KERNEL_LOG(context,
                           "Unsupported sparse fully-connected weight format.");
        return kTfLiteError;
      }

      if (sparsity.dim_metadata_size == kDimMetadataSizeRandomSparse) {
        // Random sparse.
        optimized_ops::FullyConnectedSparseWeight(
            sparsity, op_params, GetTensorShape(input),
            GetTensorData<float>(input), GetTensorShape(filter),
            GetTensorData<float>(filter), GetTensorShape(bias),
            GetTensorData<float>(bias), GetTensorShape(output),
            GetTensorData<float>(output));
      } else if (sparsity.dim_metadata_size == kDimMetadataSizeBlockSparse &&
                 sparsity.dim_metadata[2].dense_size == 4) {
        // Block sparse with block size of 1x4.
        optimized_ops::FullyConnectedSparseWeight1x4(
            sparsity, op_params, GetTensorShape(input),
            GetTensorData<float>(input), GetTensorShape(filter),
            GetTensorData<float>(filter), GetTensorShape(bias),
            GetTensorData<float>(bias), GetTensorShape(output),
            GetTensorData<float>(output),
            CpuBackendContext::GetFromContext(context));
      } else {
        TF_LITE_KERNEL_LOG(context,
                           "Unsupported sparse fully-connected weight format.");
        return kTfLiteError;
      }

    } else {
      op_params.lhs_cacheable = IsConstantTensor(filter);
      op_params.rhs_cacheable = IsConstantTensor(input);
      optimized_ops::FullyConnected(
          op_params, GetTensorShape(input), GetTensorData<float>(input),
          GetTensorShape(filter), GetTensorData<float>(filter),
          GetTensorShape(bias), GetTensorData<float>(bias),
          GetTensorShape(output), GetTensorData<float>(output),
          CpuBackendContext::GetFromContext(context));
    }
  }

  return kTfLiteOk;
}

Tensorflow is an Open Source Machine Learning Framework. An attacker can craft a TFLite model that would cause a write outside of bounds of an array in TFLite. In fact, the attacker can override the linked list used by the memory allocator. This can be leveraged for an arbitrary write primitive under certain conditions. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.


TfLiteStatus EvalFloat(TfLiteContext* context, TfLiteNode* node,
                       TfLiteFullyConnectedParams* params, OpData* data,
                       const TfLiteTensor* input, const TfLiteTensor* filter,
                       const TfLiteTensor* bias, TfLiteTensor* output) {
  float output_activation_min, output_activation_max;
  CalculateActivationRange(params->activation, &output_activation_min,
                           &output_activation_max);
  if (kernel_type == kReference) {
    FullyConnectedParams op_params;
    op_params.float_activation_min = output_activation_min;
    op_params.float_activation_max = output_activation_max;
    if (filter->sparsity != nullptr) {
      const auto& sparsity = *filter->sparsity;
      reference_ops::FullyConnectedSparseWeight(
          sparsity, op_params, GetTensorShape(input),
          GetTensorData<float>(input), GetTensorShape(filter),
          GetTensorData<float>(filter), GetTensorShape(bias),
          GetTensorData<float>(bias), GetTensorShape(output),
          GetTensorData<float>(output));
    } else {
      reference_ops::FullyConnected(
          op_params, GetTensorShape(input), GetTensorData<float>(input),
          GetTensorShape(filter), GetTensorData<float>(filter),
          GetTensorShape(bias), GetTensorData<float>(bias),
          GetTensorShape(output), GetTensorData<float>(output));
    }
  } else if (kernel_type == kLegacyPie) {
    return EvalPie(context, node, params, data, input, filter, bias, output);
  } else {
    FullyConnectedParams op_params;
    op_params.float_activation_min = output_activation_min;
    op_params.float_activation_max = output_activation_max;
    if (filter->sparsity != nullptr) {
      const auto& sparsity = *filter->sparsity;
      if (!SupportedSparsityFormat(sparsity)) {
        TF_LITE_KERNEL_LOG(context,
                           "Unsupported sparse fully-connected weight format.");
        return kTfLiteError;
      }
      const auto& input_shape = GetTensorShape(input);
      const auto& filter_shape = GetTensorShape(filter);
      const auto& output_shape = GetTensorShape(output);
      const auto& bias_shape = GetTensorShape(bias);
      if (!VerifySparsity(filter_shape, input_shape, output_shape, &sparsity)) {
        TF_LITE_KERNEL_LOG(context, "Invalid sparse fully-connected format.");
        return kTfLiteError;
      }

      if (sparsity.dim_metadata_size == kDimMetadataSizeRandomSparse) {
        // Random sparse.
        optimized_ops::FullyConnectedSparseWeight(
            sparsity, op_params,                         // Disable formatting
            input_shape, GetTensorData<float>(input),    // Disable formatting
            filter_shape, GetTensorData<float>(filter),  // Disable formatting
            bias_shape, GetTensorData<float>(bias),      // Disable formatting
            output_shape, GetTensorData<float>(output));
      } else if (sparsity.dim_metadata_size == kDimMetadataSizeBlockSparse &&
                 sparsity.dim_metadata[2].dense_size == 4) {
        // Block sparse with block size of 1x4.
        optimized_ops::FullyConnectedSparseWeight1x4(
            sparsity, op_params,                         // Disable formatting
            input_shape, GetTensorData<float>(input),    // Disable formatting
            filter_shape, GetTensorData<float>(filter),  // Disable formatting
            bias_shape, GetTensorData<float>(bias),      // Disable formatting
            output_shape, GetTensorData<float>(output),
            CpuBackendContext::GetFromContext(context));
      } else {
        TF_LITE_KERNEL_LOG(context,
                           "Unsupported sparse fully-connected weight format.");
        return kTfLiteError;
      }

    } else {
      op_params.lhs_cacheable = IsConstantTensor(filter);
      op_params.rhs_cacheable = IsConstantTensor(input);
      optimized_ops::FullyConnected(
          op_params, GetTensorShape(input), GetTensorData<float>(input),
          GetTensorShape(filter), GetTensorData<float>(filter),
          GetTensorShape(bias), GetTensorData<float>(bias),
          GetTensorShape(output), GetTensorData<float>(output),
          CpuBackendContext::GetFromContext(context));
    }
  }

  return kTfLiteOk;
}


uint8_t ethereum_extractThorchainData(const EthereumSignTx *msg,
                                          char *buffer) {
  // Swap data begins 164 chars into data buffer:
  // offset = deposit function hash + address + address + uint256
  uint16_t offset = 4 + (5 * 32);
  int16_t len = msg->data_length - offset;
  if (msg->has_data_length && len > 0) {
    memcpy(buffer, msg->data_initial_chunk.bytes + offset, len);
    // String length must be < 255 characters
    return len < 256 ? (uint8_t)len : 0;
  }
  return 0;
}

Insufficient length checks in the ShapeShift KeepKey hardware wallet firmware before 7.1.0 allow a stack buffer overflow via crafted messages. The overflow in ethereum_extractThorchainSwapData() in ethereum.c can circumvent stack protections and lead to code execution. The vulnerable interface is reachable remotely over WebUSB.


uint8_t ethereum_extractThorchainData(const EthereumSignTx *msg,
                                          char *buffer) {
  // Swap data begins 164 chars into data buffer:
  // offset = deposit function hash + address + address + uint256
  uint16_t offset = 4 + (5 * 32);
  int16_t len = msg->data_length - offset;
  if (msg->has_data_length && len > 0 && len < 256) {
    memcpy(buffer, msg->data_initial_chunk.bytes + offset, len);
    // String length must be < 255 characters
    return (uint8_t)len;
  }
  return 0;
}


fix heap-buffer-overflow in mp4read.c

This originated from an integer overflow: If mp4config.frame.ents
would be read-in with a value of (uint32t)(-1), it would overflow to 0
in the size calculation for the allocation in the next line. The
malloc() function would then successfully return a pointer to a memory
region of size 0, which will cause a segfault when written to.

Fixes #57.

static int stszin(int size)
{
    int cnt;
    uint32_t ofs;

    // version/flags
    u32in();
    // Sample size
    u32in();
    // Number of entries
    mp4config.frame.ents = u32in();
    // fixme: check atom size
    mp4config.frame.data = malloc(sizeof(*mp4config.frame.data)
                                  * (mp4config.frame.ents + 1));

    if (!mp4config.frame.data)
        return ERR_FAIL;

    ofs = 0;
    mp4config.frame.data[0] = ofs;
    for (cnt = 0; cnt < mp4config.frame.ents; cnt++)
    {
        uint32_t fsize = u32in();

        ofs += fsize;
        if (mp4config.frame.maxsize < fsize)
            mp4config.frame.maxsize = fsize;

        mp4config.frame.data[cnt + 1] = ofs;

        if (ofs < mp4config.frame.data[cnt])
            return ERR_FAIL;
    }

    return size;
}

An issue was discovered in faad2 before 2.10.0. A heap-buffer-overflow exists in the function stszin located in mp4read.c. It allows an attacker to cause Code Execution.


static int stszin(int size)
{
    int cnt;
    uint32_t ofs;

    // version/flags
    u32in();
    // Sample size
    u32in();
    // Number of entries
    mp4config.frame.ents = u32in();

    if (!(mp4config.frame.ents + 1))
        return ERR_FAIL;

    mp4config.frame.data = malloc(sizeof(*mp4config.frame.data)
                                  * (mp4config.frame.ents + 1));

    if (!mp4config.frame.data)
        return ERR_FAIL;

    ofs = 0;
    mp4config.frame.data[0] = ofs;
    for (cnt = 0; cnt < mp4config.frame.ents; cnt++)
    {
        uint32_t fsize = u32in();

        ofs += fsize;
        if (mp4config.frame.maxsize < fsize)
            mp4config.frame.maxsize = fsize;

        mp4config.frame.data[cnt + 1] = ofs;

        if (ofs < mp4config.frame.data[cnt])
            return ERR_FAIL;
    }

    return size;
}


proc.c: should not reference `irep` when copying failed.

It may cause broken reference count numbers.

mrb_proc_copy(mrb_state *mrb, struct RProc *a, struct RProc *b)
{
  if (a->body.irep) {
    /* already initialized proc */
    return;
  }
  a->flags = b->flags;
  a->body = b->body;
  a->upper = b->upper;
  if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
    mrb_irep_incref(mrb, (mrb_irep*)a->body.irep);
  }
  a->e.env = b->e.env;
  /* a->e.target_class = a->e.target_class; */
}

mruby is vulnerable to Heap-based Buffer Overflow


mrb_proc_copy(mrb_state *mrb, struct RProc *a, struct RProc *b)
{
  if (a->body.irep) {
    /* already initialized proc */
    return;
  }
  if (!MRB_PROC_CFUNC_P(b) && b->body.irep) {
    mrb_irep_incref(mrb, (mrb_irep*)b->body.irep);
  }
  a->flags = b->flags;
  a->body = b->body;
  a->upper = b->upper;
  a->e.env = b->e.env;
  /* a->e.target_class = a->e.target_class; */
}


Use `PartialTensorShape` instead of `TensorShape`.

`TensorShape` constructor throws a CHECK-fail if shape is partial/overflows which the other doesn't. We are only determining the number of elements in the shape and partial shape should be used as it returns negative number when needed.

PiperOrigin-RevId: 409205384
Change-Id: Ia56542ff9ec758f2c9ffc7e4dcc9fa7eecd86e7b

int64_t TensorByteSize(const TensorProto& t) {
  // num_elements returns -1 if shape is not fully defined.
  int64_t num_elems = TensorShape(t.tensor_shape()).num_elements();
  return num_elems < 0 ? -1 : num_elems * DataTypeSize(t.dtype());
}

Tensorflow is an Open Source Machine Learning Framework. A malicious user can cause a denial of service by altering a `SavedModel` such that `TensorByteSize` would trigger `CHECK` failures. `TensorShape` constructor throws a `CHECK`-fail if shape is partial or has a number of elements that would overflow the size of an `int`. The `PartialTensorShape` constructor instead does not cause a `CHECK`-abort if the shape is partial, which is exactly what this function needs to be able to return `-1`. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.


int64_t TensorByteSize(const TensorProto& t) {
  // num_elements returns -1 if shape is not fully defined.
  int64_t num_elems = PartialTensorShape(t.tensor_shape()).num_elements();
  return num_elems < 0 ? -1 : num_elems * DataTypeSize(t.dtype());
}


[lite] Add validation check for dilation height/width to be positive integers.

PiperOrigin-RevId: 416429178
Change-Id: If7cdcddca54486434d9b2f06e7e2b401d7c3ee25

TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  auto* params =
      reinterpret_cast<TfLiteDepthwiseConvParams*>(node->builtin_data);
  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  bool has_bias = NumInputs(node) == 3;

  TF_LITE_ENSURE(context, has_bias || NumInputs(node) == 2);
  const TfLiteTensor* input;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input));
  const TfLiteTensor* filter;
  TF_LITE_ENSURE_OK(context,
                    GetInputSafe(context, node, kFilterTensor, &filter));
  const TfLiteTensor* bias = nullptr;

  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context,
                    GetOutputSafe(context, node, kOutputTensor, &output));

  TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
  TF_LITE_ENSURE_EQ(context, NumDimensions(filter), 4);

  const TfLiteType data_type = input->type;

  const TfLiteType filter_type = filter->type;
  const bool is_hybrid =
      data_type == kTfLiteFloat32 && filter_type == kTfLiteInt8;
  TF_LITE_ENSURE(context,
                 data_type == kTfLiteFloat32 || data_type == kTfLiteUInt8 ||
                     data_type == kTfLiteInt8 || data_type == kTfLiteInt16);
  TF_LITE_ENSURE_TYPES_EQ(context, output->type, data_type);
  if (!is_hybrid) {
    TF_LITE_ENSURE(context,
                   filter->type == data_type || data_type == kTfLiteInt16);
  }

  if (data_type == kTfLiteInt16) {
    TF_LITE_ENSURE_EQ(context, input->params.zero_point, 0);
    TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0);
  }

  // Filter in DepthwiseConv is expected to be [1, H, W, O].
  TF_LITE_ENSURE_EQ(context, SizeOfDimension(filter, 0), 1);

  if (has_bias) {
    TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kBiasTensor, &bias));
    if (data_type == kTfLiteUInt8 || data_type == kTfLiteInt8) {
      TF_LITE_ENSURE_TYPES_EQ(context, bias->type, kTfLiteInt32);
      TF_LITE_ENSURE_EQ(context, bias->params.zero_point, 0);
    } else if (data_type == kTfLiteInt16) {
      TF_LITE_ENSURE_TYPES_EQ(context, bias->type, kTfLiteInt64);
      TF_LITE_ENSURE_EQ(context, bias->params.zero_point, 0);
    } else {
      TF_LITE_ENSURE_TYPES_EQ(context, bias->type, data_type);
    }
    TF_LITE_ENSURE_EQ(context, NumDimensions(bias), 1);
    TF_LITE_ENSURE_EQ(context, SizeOfDimension(filter, 3),
                      SizeOfDimension(bias, 0));
  }

  int channels_out = SizeOfDimension(filter, 3);
  int width = SizeOfDimension(input, 2);
  int height = SizeOfDimension(input, 1);
  int filter_width = SizeOfDimension(filter, 2);
  int filter_height = SizeOfDimension(filter, 1);
  int batches = SizeOfDimension(input, 0);

  // Matching GetWindowedOutputSize in TensorFlow.
  auto padding = params->padding;
  int out_width, out_height;

  data->padding = ComputePaddingHeightWidth(
      params->stride_height, params->stride_width,
      params->dilation_height_factor, params->dilation_width_factor, height,
      width, filter_height, filter_width, padding, &out_height, &out_width);

  // Note that quantized inference requires that all tensors have their
  // parameters set. This is usually done during quantized training or
  // calibration.
  if (data_type != kTfLiteFloat32) {
    TF_LITE_ENSURE_EQ(context, filter->quantization.type,
                      kTfLiteAffineQuantization);
    TF_LITE_ENSURE(context, filter->quantization.type != kTfLiteNoQuantization);
    const auto* affine_quantization =
        reinterpret_cast<TfLiteAffineQuantization*>(
            filter->quantization.params);
    TF_LITE_ENSURE(context, affine_quantization);
    TF_LITE_ENSURE(context, affine_quantization->scale);
    TF_LITE_ENSURE(context, (affine_quantization->scale->size == 1 ||
                             affine_quantization->scale->size == channels_out));

    data->per_channel_output_multiplier.resize(channels_out);
    data->per_channel_output_shift.resize(channels_out);
    TF_LITE_ENSURE_STATUS(tflite::PopulateConvolutionQuantizationParams(
        context, input, filter, bias, output, params->activation,
        &data->output_multiplier, &data->output_shift,
        &data->output_activation_min, &data->output_activation_max,
        data->per_channel_output_multiplier.data(),
        data->per_channel_output_shift.data(), channels_out));
  }

  if (is_hybrid) {
    TF_LITE_ENSURE(context, filter->quantization.type != kTfLiteNoQuantization);
    const auto* affine_quantization =
        reinterpret_cast<TfLiteAffineQuantization*>(
            filter->quantization.params);
    TF_LITE_ENSURE(context, affine_quantization);
    TF_LITE_ENSURE(context, affine_quantization->scale);
    TF_LITE_ENSURE_EQ(
        context, affine_quantization->scale->size,
        filter->dims->data[affine_quantization->quantized_dimension]);

    int temporaries_count = 0;
    data->input_quantized_index = temporaries_count;
    if (data->input_quantized_id == kTensorNotAllocated) {
      TF_LITE_ENSURE_OK(
          context, context->AddTensors(context, 1, &data->input_quantized_id));
    }
    ++temporaries_count;
    data->scaling_factors_index = temporaries_count;
    if (data->scaling_factors_id == kTensorNotAllocated) {
      TF_LITE_ENSURE_OK(
          context, context->AddTensors(context, 1, &data->scaling_factors_id));
    }
    ++temporaries_count;
    data->input_offset_index = temporaries_count;
    if (data->input_offset_id == kTensorNotAllocated) {
      TF_LITE_ENSURE_OK(
          context, context->AddTensors(context, 1, &data->input_offset_id));
    }
    ++temporaries_count;

    TfLiteIntArrayFree(node->temporaries);
    node->temporaries = TfLiteIntArrayCreate(temporaries_count);

    node->temporaries->data[data->input_quantized_index] =
        data->input_quantized_id;
    TfLiteTensor* input_quantized;
    TF_LITE_ENSURE_OK(
        context, GetTemporarySafe(context, node, data->input_quantized_index,
                                  &input_quantized));
    input_quantized->type = kTfLiteInt8;
    input_quantized->allocation_type = kTfLiteArenaRw;
    if (!TfLiteIntArrayEqual(input_quantized->dims, input->dims)) {
      TfLiteIntArray* input_quantized_size = TfLiteIntArrayCopy(input->dims);
      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, input_quantized,
                                                       input_quantized_size));
    }
    node->temporaries->data[data->scaling_factors_index] =
        data->scaling_factors_id;
    TfLiteTensor* scaling_factors;
    TF_LITE_ENSURE_OK(
        context, GetTemporarySafe(context, node, data->scaling_factors_index,
                                  &scaling_factors));
    scaling_factors->type = kTfLiteFloat32;
    scaling_factors->allocation_type = kTfLiteArenaRw;
    const int batch_size = SizeOfDimension(input, 0);
    int scaling_dims[1] = {batch_size};
    if (!TfLiteIntArrayEqualsArray(scaling_factors->dims, 1, scaling_dims)) {
      TfLiteIntArray* scaling_factors_size = TfLiteIntArrayCreate(1);
      scaling_factors_size->data[0] = batch_size;
      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, scaling_factors,
                                                       scaling_factors_size));
    }
    node->temporaries->data[data->input_offset_index] = data->input_offset_id;
    TfLiteTensor* input_offsets;
    TF_LITE_ENSURE_OK(context,
                      GetTemporarySafe(context, node, data->input_offset_index,
                                       &input_offsets));
    input_offsets->type = kTfLiteInt32;
    input_offsets->allocation_type = kTfLiteArenaRw;
    if (!TfLiteIntArrayEqualsArray(input_offsets->dims, 1, scaling_dims)) {
      TfLiteIntArray* input_offsets_size = TfLiteIntArrayCreate(1);
      input_offsets_size->data[0] = batch_size;
      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, input_offsets,
                                                       input_offsets_size));
    }
  }

  TfLiteIntArray* outputSize = TfLiteIntArrayCreate(4);
  outputSize->data[0] = batches;
  outputSize->data[1] = out_height;
  outputSize->data[2] = out_width;
  outputSize->data[3] = channels_out;
  return context->ResizeTensor(context, output, outputSize);
}

Tensorflow is an Open Source Machine Learning Framework. ### Impact An attacker can craft a TFLite model that would trigger a division by zero in the implementation of depthwise convolutions. The parameters of the convolution can be user controlled and are also used within a division operation to determine the size of the padding that needs to be added before applying the convolution. There is no check before this division that the divisor is strictly positive. The fix will be included in TensorFlow 2.8.0. We will also cherrypick this commit on TensorFlow 2.7.1, TensorFlow 2.6.3, and TensorFlow 2.5.3, as these are also affected and still in supported range.


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  auto* params =
      reinterpret_cast<TfLiteDepthwiseConvParams*>(node->builtin_data);
  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  bool has_bias = NumInputs(node) == 3;

  TF_LITE_ENSURE(context, has_bias || NumInputs(node) == 2);
  const TfLiteTensor* input;
  TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input));
  const TfLiteTensor* filter;
  TF_LITE_ENSURE_OK(context,
                    GetInputSafe(context, node, kFilterTensor, &filter));
  const TfLiteTensor* bias = nullptr;

  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context,
                    GetOutputSafe(context, node, kOutputTensor, &output));

  TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
  TF_LITE_ENSURE_EQ(context, NumDimensions(filter), 4);
  TF_LITE_ENSURE(context, params->dilation_height_factor > 0);
  TF_LITE_ENSURE(context, params->dilation_width_factor > 0);

  const TfLiteType data_type = input->type;

  const TfLiteType filter_type = filter->type;
  const bool is_hybrid =
      data_type == kTfLiteFloat32 && filter_type == kTfLiteInt8;
  TF_LITE_ENSURE(context,
                 data_type == kTfLiteFloat32 || data_type == kTfLiteUInt8 ||
                     data_type == kTfLiteInt8 || data_type == kTfLiteInt16);
  TF_LITE_ENSURE_TYPES_EQ(context, output->type, data_type);
  if (!is_hybrid) {
    TF_LITE_ENSURE(context,
                   filter->type == data_type || data_type == kTfLiteInt16);
  }

  if (data_type == kTfLiteInt16) {
    TF_LITE_ENSURE_EQ(context, input->params.zero_point, 0);
    TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0);
  }

  // Filter in DepthwiseConv is expected to be [1, H, W, O].
  TF_LITE_ENSURE_EQ(context, SizeOfDimension(filter, 0), 1);

  if (has_bias) {
    TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kBiasTensor, &bias));
    if (data_type == kTfLiteUInt8 || data_type == kTfLiteInt8) {
      TF_LITE_ENSURE_TYPES_EQ(context, bias->type, kTfLiteInt32);
      TF_LITE_ENSURE_EQ(context, bias->params.zero_point, 0);
    } else if (data_type == kTfLiteInt16) {
      TF_LITE_ENSURE_TYPES_EQ(context, bias->type, kTfLiteInt64);
      TF_LITE_ENSURE_EQ(context, bias->params.zero_point, 0);
    } else {
      TF_LITE_ENSURE_TYPES_EQ(context, bias->type, data_type);
    }
    TF_LITE_ENSURE_EQ(context, NumDimensions(bias), 1);
    TF_LITE_ENSURE_EQ(context, SizeOfDimension(filter, 3),
                      SizeOfDimension(bias, 0));
  }

  int channels_out = SizeOfDimension(filter, 3);
  int width = SizeOfDimension(input, 2);
  int height = SizeOfDimension(input, 1);
  int filter_width = SizeOfDimension(filter, 2);
  int filter_height = SizeOfDimension(filter, 1);
  int batches = SizeOfDimension(input, 0);

  // Matching GetWindowedOutputSize in TensorFlow.
  auto padding = params->padding;
  int out_width, out_height;

  data->padding = ComputePaddingHeightWidth(
      params->stride_height, params->stride_width,
      params->dilation_height_factor, params->dilation_width_factor, height,
      width, filter_height, filter_width, padding, &out_height, &out_width);

  // Note that quantized inference requires that all tensors have their
  // parameters set. This is usually done during quantized training or
  // calibration.
  if (data_type != kTfLiteFloat32) {
    TF_LITE_ENSURE_EQ(context, filter->quantization.type,
                      kTfLiteAffineQuantization);
    TF_LITE_ENSURE(context, filter->quantization.type != kTfLiteNoQuantization);
    const auto* affine_quantization =
        reinterpret_cast<TfLiteAffineQuantization*>(
            filter->quantization.params);
    TF_LITE_ENSURE(context, affine_quantization);
    TF_LITE_ENSURE(context, affine_quantization->scale);
    TF_LITE_ENSURE(context, (affine_quantization->scale->size == 1 ||
                             affine_quantization->scale->size == channels_out));

    data->per_channel_output_multiplier.resize(channels_out);
    data->per_channel_output_shift.resize(channels_out);
    TF_LITE_ENSURE_STATUS(tflite::PopulateConvolutionQuantizationParams(
        context, input, filter, bias, output, params->activation,
        &data->output_multiplier, &data->output_shift,
        &data->output_activation_min, &data->output_activation_max,
        data->per_channel_output_multiplier.data(),
        data->per_channel_output_shift.data(), channels_out));
  }

  if (is_hybrid) {
    TF_LITE_ENSURE(context, filter->quantization.type != kTfLiteNoQuantization);
    const auto* affine_quantization =
        reinterpret_cast<TfLiteAffineQuantization*>(
            filter->quantization.params);
    TF_LITE_ENSURE(context, affine_quantization);
    TF_LITE_ENSURE(context, affine_quantization->scale);
    TF_LITE_ENSURE_EQ(
        context, affine_quantization->scale->size,
        filter->dims->data[affine_quantization->quantized_dimension]);

    int temporaries_count = 0;
    data->input_quantized_index = temporaries_count;
    if (data->input_quantized_id == kTensorNotAllocated) {
      TF_LITE_ENSURE_OK(
          context, context->AddTensors(context, 1, &data->input_quantized_id));
    }
    ++temporaries_count;
    data->scaling_factors_index = temporaries_count;
    if (data->scaling_factors_id == kTensorNotAllocated) {
      TF_LITE_ENSURE_OK(
          context, context->AddTensors(context, 1, &data->scaling_factors_id));
    }
    ++temporaries_count;
    data->input_offset_index = temporaries_count;
    if (data->input_offset_id == kTensorNotAllocated) {
      TF_LITE_ENSURE_OK(
          context, context->AddTensors(context, 1, &data->input_offset_id));
    }
    ++temporaries_count;

    TfLiteIntArrayFree(node->temporaries);
    node->temporaries = TfLiteIntArrayCreate(temporaries_count);

    node->temporaries->data[data->input_quantized_index] =
        data->input_quantized_id;
    TfLiteTensor* input_quantized;
    TF_LITE_ENSURE_OK(
        context, GetTemporarySafe(context, node, data->input_quantized_index,
                                  &input_quantized));
    input_quantized->type = kTfLiteInt8;
    input_quantized->allocation_type = kTfLiteArenaRw;
    if (!TfLiteIntArrayEqual(input_quantized->dims, input->dims)) {
      TfLiteIntArray* input_quantized_size = TfLiteIntArrayCopy(input->dims);
      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, input_quantized,
                                                       input_quantized_size));
    }
    node->temporaries->data[data->scaling_factors_index] =
        data->scaling_factors_id;
    TfLiteTensor* scaling_factors;
    TF_LITE_ENSURE_OK(
        context, GetTemporarySafe(context, node, data->scaling_factors_index,
                                  &scaling_factors));
    scaling_factors->type = kTfLiteFloat32;
    scaling_factors->allocation_type = kTfLiteArenaRw;
    const int batch_size = SizeOfDimension(input, 0);
    int scaling_dims[1] = {batch_size};
    if (!TfLiteIntArrayEqualsArray(scaling_factors->dims, 1, scaling_dims)) {
      TfLiteIntArray* scaling_factors_size = TfLiteIntArrayCreate(1);
      scaling_factors_size->data[0] = batch_size;
      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, scaling_factors,
                                                       scaling_factors_size));
    }
    node->temporaries->data[data->input_offset_index] = data->input_offset_id;
    TfLiteTensor* input_offsets;
    TF_LITE_ENSURE_OK(context,
                      GetTemporarySafe(context, node, data->input_offset_index,
                                       &input_offsets));
    input_offsets->type = kTfLiteInt32;
    input_offsets->allocation_type = kTfLiteArenaRw;
    if (!TfLiteIntArrayEqualsArray(input_offsets->dims, 1, scaling_dims)) {
      TfLiteIntArray* input_offsets_size = TfLiteIntArrayCreate(1);
      input_offsets_size->data[0] = batch_size;
      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, input_offsets,
                                                       input_offsets_size));
    }
  }

  TfLiteIntArray* outputSize = TfLiteIntArrayCreate(4);
  outputSize->data[0] = batches;
  outputSize->data[1] = out_height;
  outputSize->data[2] = out_width;
  outputSize->data[3] = channels_out;
  return context->ResizeTensor(context, output, outputSize);
}


phonet: refcount leak in pep_sock_accep

sock_hold(sk) is invoked in pep_sock_accept(), but __sock_put(sk) is not
invoked in subsequent failure branches(pep_accept_conn() != 0).

Signed-off-by: Hangyu Hua <hbh25y@gmail.com>
Link: https://lore.kernel.org/r/20211209082839.33985-1-hbh25y@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp,
				    bool kern)
{
	struct pep_sock *pn = pep_sk(sk), *newpn;
	struct sock *newsk = NULL;
	struct sk_buff *skb;
	struct pnpipehdr *hdr;
	struct sockaddr_pn dst, src;
	int err;
	u16 peer_type;
	u8 pipe_handle, enabled, n_sb;
	u8 aligned = 0;

	skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp);
	if (!skb)
		return NULL;

	lock_sock(sk);
	if (sk->sk_state != TCP_LISTEN) {
		err = -EINVAL;
		goto drop;
	}
	sk_acceptq_removed(sk);

	err = -EPROTO;
	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
		goto drop;

	hdr = pnp_hdr(skb);
	pipe_handle = hdr->pipe_handle;
	switch (hdr->state_after_connect) {
	case PN_PIPE_DISABLE:
		enabled = 0;
		break;
	case PN_PIPE_ENABLE:
		enabled = 1;
		break;
	default:
		pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM,
				GFP_KERNEL);
		goto drop;
	}
	peer_type = hdr->other_pep_type << 8;

	/* Parse sub-blocks (options) */
	n_sb = hdr->data[3];
	while (n_sb > 0) {
		u8 type, buf[1], len = sizeof(buf);
		const u8 *data = pep_get_sb(skb, &type, &len, buf);

		if (data == NULL)
			goto drop;
		switch (type) {
		case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
			if (len < 1)
				goto drop;
			peer_type = (peer_type & 0xff00) | data[0];
			break;
		case PN_PIPE_SB_ALIGNED_DATA:
			aligned = data[0] != 0;
			break;
		}
		n_sb--;
	}

	/* Check for duplicate pipe handle */
	newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
	if (unlikely(newsk)) {
		__sock_put(newsk);
		newsk = NULL;
		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
		goto drop;
	}

	/* Create a new to-be-accepted sock */
	newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot,
			 kern);
	if (!newsk) {
		pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL);
		err = -ENOBUFS;
		goto drop;
	}

	sock_init_data(NULL, newsk);
	newsk->sk_state = TCP_SYN_RECV;
	newsk->sk_backlog_rcv = pipe_do_rcv;
	newsk->sk_protocol = sk->sk_protocol;
	newsk->sk_destruct = pipe_destruct;

	newpn = pep_sk(newsk);
	pn_skb_get_dst_sockaddr(skb, &dst);
	pn_skb_get_src_sockaddr(skb, &src);
	newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
	newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
	newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
	sock_hold(sk);
	newpn->listener = sk;
	skb_queue_head_init(&newpn->ctrlreq_queue);
	newpn->pipe_handle = pipe_handle;
	atomic_set(&newpn->tx_credits, 0);
	newpn->ifindex = 0;
	newpn->peer_type = peer_type;
	newpn->rx_credits = 0;
	newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
	newpn->init_enable = enabled;
	newpn->aligned = aligned;

	err = pep_accept_conn(newsk, skb);
	if (err) {
		sock_put(newsk);
		newsk = NULL;
		goto drop;
	}
	sk_add_node(newsk, &pn->hlist);
drop:
	release_sock(sk);
	kfree_skb(skb);
	*errp = err;
	return newsk;
}

pep_sock_accept in net/phonet/pep.c in the Linux kernel through 5.15.8 has a refcount leak.


static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp,
				    bool kern)
{
	struct pep_sock *pn = pep_sk(sk), *newpn;
	struct sock *newsk = NULL;
	struct sk_buff *skb;
	struct pnpipehdr *hdr;
	struct sockaddr_pn dst, src;
	int err;
	u16 peer_type;
	u8 pipe_handle, enabled, n_sb;
	u8 aligned = 0;

	skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp);
	if (!skb)
		return NULL;

	lock_sock(sk);
	if (sk->sk_state != TCP_LISTEN) {
		err = -EINVAL;
		goto drop;
	}
	sk_acceptq_removed(sk);

	err = -EPROTO;
	if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
		goto drop;

	hdr = pnp_hdr(skb);
	pipe_handle = hdr->pipe_handle;
	switch (hdr->state_after_connect) {
	case PN_PIPE_DISABLE:
		enabled = 0;
		break;
	case PN_PIPE_ENABLE:
		enabled = 1;
		break;
	default:
		pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM,
				GFP_KERNEL);
		goto drop;
	}
	peer_type = hdr->other_pep_type << 8;

	/* Parse sub-blocks (options) */
	n_sb = hdr->data[3];
	while (n_sb > 0) {
		u8 type, buf[1], len = sizeof(buf);
		const u8 *data = pep_get_sb(skb, &type, &len, buf);

		if (data == NULL)
			goto drop;
		switch (type) {
		case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE:
			if (len < 1)
				goto drop;
			peer_type = (peer_type & 0xff00) | data[0];
			break;
		case PN_PIPE_SB_ALIGNED_DATA:
			aligned = data[0] != 0;
			break;
		}
		n_sb--;
	}

	/* Check for duplicate pipe handle */
	newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
	if (unlikely(newsk)) {
		__sock_put(newsk);
		newsk = NULL;
		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL);
		goto drop;
	}

	/* Create a new to-be-accepted sock */
	newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot,
			 kern);
	if (!newsk) {
		pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL);
		err = -ENOBUFS;
		goto drop;
	}

	sock_init_data(NULL, newsk);
	newsk->sk_state = TCP_SYN_RECV;
	newsk->sk_backlog_rcv = pipe_do_rcv;
	newsk->sk_protocol = sk->sk_protocol;
	newsk->sk_destruct = pipe_destruct;

	newpn = pep_sk(newsk);
	pn_skb_get_dst_sockaddr(skb, &dst);
	pn_skb_get_src_sockaddr(skb, &src);
	newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
	newpn->pn_sk.dobject = pn_sockaddr_get_object(&src);
	newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);
	sock_hold(sk);
	newpn->listener = sk;
	skb_queue_head_init(&newpn->ctrlreq_queue);
	newpn->pipe_handle = pipe_handle;
	atomic_set(&newpn->tx_credits, 0);
	newpn->ifindex = 0;
	newpn->peer_type = peer_type;
	newpn->rx_credits = 0;
	newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
	newpn->init_enable = enabled;
	newpn->aligned = aligned;

	err = pep_accept_conn(newsk, skb);
	if (err) {
		__sock_put(sk);
		sock_put(newsk);
		newsk = NULL;
		goto drop;
	}
	sk_add_node(newsk, &pn->hlist);
drop:
	release_sock(sk);
	kfree_skb(skb);
	*errp = err;
	return newsk;
}


GF_Err gf_isom_box_parse_ex(GF_Box **outBox, GF_BitStream *bs, u32 parent_type, Bool is_root_box)
{
	u32 type, uuid_type, hdr_size;
	u64 size, start, payload_start, end;
	char uuid[16];
	GF_Err e;
	GF_Box *newBox;
	Bool skip_logs = gf_bs_get_cookie(bs) ? GF_TRUE : GF_FALSE;
	Bool is_special = GF_TRUE;

	if ((bs == NULL) || (outBox == NULL) ) return GF_BAD_PARAM;
	*outBox = NULL;
	if (gf_bs_available(bs) < 8) {
		return GF_ISOM_INCOMPLETE_FILE;
	}

	start = gf_bs_get_position(bs);

	uuid_type = 0;
	size = (u64) gf_bs_read_u32(bs);
	hdr_size = 4;
	/*fix for some boxes found in some old hinted files*/
	if ((size >= 2) && (size <= 4)) {
		size = 4;
		type = GF_ISOM_BOX_TYPE_VOID;
	} else {
		type = gf_bs_read_u32(bs);
		hdr_size += 4;
		/*no size means till end of file - EXCEPT FOR some old QuickTime boxes...*/
		if (type == GF_ISOM_BOX_TYPE_TOTL)
			size = 12;
		if (!size) {
			if (is_root_box) {
				if (!skip_logs) {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning Read Box type %s (0x%08X) size 0 reading till the end of file\n", gf_4cc_to_str(type), type));
				}
				size = gf_bs_available(bs) + 8;
			} else {
				if (!skip_logs) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Read Box type %s (0x%08X) at position "LLU" has size 0 but is not at root/file level, skipping\n", gf_4cc_to_str(type), type, start));
				}
				return GF_OK;
//				return GF_ISOM_INVALID_FILE;
			}
		}
	}
	/*handle uuid*/
	memset(uuid, 0, 16);
	if (type == GF_ISOM_BOX_TYPE_UUID ) {
		if (gf_bs_available(bs) < 16) {
			return GF_ISOM_INCOMPLETE_FILE;
		}
		gf_bs_read_data(bs, uuid, 16);
		hdr_size += 16;
		uuid_type = gf_isom_solve_uuid_box(uuid);
	}

	//handle large box
	if (size == 1) {
		if (gf_bs_available(bs) < 8) {
			return GF_ISOM_INCOMPLETE_FILE;
		}
		size = gf_bs_read_u64(bs);
		hdr_size += 8;
	}
	GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Read Box type %s size "LLD" start "LLD"\n", gf_4cc_to_str(type), LLD_CAST size, LLD_CAST start));

	if ( size < hdr_size ) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Box size "LLD" less than box header size %d\n", LLD_CAST size, hdr_size));
		return GF_ISOM_INVALID_FILE;
	}

	//some special boxes (references and track groups) are handled by a single generic box with an associated ref/group type
	if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_TREF)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_TrackReferenceTypeBox*)newBox)->reference_type = type;
	} else if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_IREF)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_REFI);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_ItemReferenceTypeBox*)newBox)->reference_type = type;
	} else if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_TRGR)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_TRGT);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_TrackGroupTypeBox*)newBox)->group_type = type;
	} else if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_GRPL)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_GRPT);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_EntityToGroupTypeBox*)newBox)->grouping_type = type;
	} else {
		//OK, create the box based on the type
		is_special = GF_FALSE;
		newBox = gf_isom_box_new_ex(uuid_type ? uuid_type : type, parent_type, skip_logs, is_root_box);
		if (!newBox) return GF_OUT_OF_MEM;
	}

	//OK, init and read this box
	if (type==GF_ISOM_BOX_TYPE_UUID && !is_special) {
		memcpy(((GF_UUIDBox *)newBox)->uuid, uuid, 16);
		((GF_UUIDBox *)newBox)->internal_4cc = uuid_type;
	}

	if (!newBox->type) newBox->type = type;
	payload_start = gf_bs_get_position(bs);

retry_unknown_box:

	end = gf_bs_available(bs);
	if (size - hdr_size > end ) {
		newBox->size = size - hdr_size - end;
		*outBox = newBox;
		return GF_ISOM_INCOMPLETE_FILE;
	}

	newBox->size = size - hdr_size;

	if (newBox->size) {
		e = gf_isom_full_box_read(newBox, bs);
		if (!e) e = gf_isom_box_read(newBox, bs);
		newBox->size = size;
		end = gf_bs_get_position(bs);
	} else {
		newBox->size = size;
		//empty box
		e = GF_OK;
		end = gf_bs_get_position(bs);
	}

	if (e && (e != GF_ISOM_INCOMPLETE_FILE)) {
		gf_isom_box_del(newBox);
		*outBox = NULL;

		if (parent_type==GF_ISOM_BOX_TYPE_STSD) {
			newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
			((GF_UnknownBox *)newBox)->original_4cc = type;
			newBox->size = size;
			gf_bs_seek(bs, payload_start);
			goto retry_unknown_box;
		}
		if (!skip_logs) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Read Box \"%s\" (start "LLU") failed (%s) - skipping\n", gf_4cc_to_str(type), start, gf_error_to_string(e)));
		}
		//we don't try to reparse known boxes that have been failing (too dangerous)
		return e;
	}

	if (end-start > size) {
		if (!skip_logs) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" size "LLU" (start "LLU") invalid (read "LLU")\n", gf_4cc_to_str(type), LLU_CAST size, start, LLU_CAST (end-start) ));
		}
		/*let's still try to load the file since no error was notified*/
		gf_bs_seek(bs, start+size);
	} else if (end-start < size) {
		u32 to_skip = (u32) (size-(end-start));
		if (!skip_logs) {
			if ((to_skip!=4) || gf_bs_peek_bits(bs, 32, 0)) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" (start "LLU") has %u extra bytes\n", gf_4cc_to_str(type), start, to_skip));
			}
		}
		gf_bs_skip_bytes(bs, to_skip);
	}
	*outBox = newBox;

	return e;
}

A Null pointer dereference vulnerability exits in MP4Box - GPAC version 0.8.0-rev177-g51a8ef874-master via the gf_isom_get_track_id function, which causes a denial of service.


GF_Err gf_isom_box_parse_ex(GF_Box **outBox, GF_BitStream *bs, u32 parent_type, Bool is_root_box)
{
	u32 type, uuid_type, hdr_size;
	u64 size, start, payload_start, end;
	char uuid[16];
	GF_Err e;
	GF_Box *newBox;
	Bool skip_logs = gf_bs_get_cookie(bs) ? GF_TRUE : GF_FALSE;
	Bool is_special = GF_TRUE;

	if ((bs == NULL) || (outBox == NULL) ) return GF_BAD_PARAM;
	*outBox = NULL;
	if (gf_bs_available(bs) < 8) {
		return GF_ISOM_INCOMPLETE_FILE;
	}

	start = gf_bs_get_position(bs);

	uuid_type = 0;
	size = (u64) gf_bs_read_u32(bs);
	hdr_size = 4;
	/*fix for some boxes found in some old hinted files*/
	if ((size >= 2) && (size <= 4)) {
		size = 4;
		type = GF_ISOM_BOX_TYPE_VOID;
	} else {
		type = gf_bs_read_u32(bs);
		hdr_size += 4;
		/*no size means till end of file - EXCEPT FOR some old QuickTime boxes...*/
		if (type == GF_ISOM_BOX_TYPE_TOTL)
			size = 12;
		if (!size) {
			if (is_root_box) {
				if (!skip_logs) {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning Read Box type %s (0x%08X) size 0 reading till the end of file\n", gf_4cc_to_str(type), type));
				}
				size = gf_bs_available(bs) + 8;
			} else {
				if (!skip_logs) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Read Box type %s (0x%08X) at position "LLU" has size 0 but is not at root/file level, skipping\n", gf_4cc_to_str(type), type, start));
				}
				return GF_OK;
//				return GF_ISOM_INVALID_FILE;
			}
		}
	}
	/*handle uuid*/
	memset(uuid, 0, 16);
	if (type == GF_ISOM_BOX_TYPE_UUID ) {
		if (gf_bs_available(bs) < 16) {
			return GF_ISOM_INCOMPLETE_FILE;
		}
		gf_bs_read_data(bs, uuid, 16);
		hdr_size += 16;
		uuid_type = gf_isom_solve_uuid_box(uuid);
	}

	//handle large box
	if (size == 1) {
		if (gf_bs_available(bs) < 8) {
			return GF_ISOM_INCOMPLETE_FILE;
		}
		size = gf_bs_read_u64(bs);
		hdr_size += 8;
	}
	GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Read Box type %s size "LLD" start "LLD"\n", gf_4cc_to_str(type), LLD_CAST size, LLD_CAST start));

	if ( size < hdr_size ) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Box size "LLD" less than box header size %d\n", LLD_CAST size, hdr_size));
		return GF_ISOM_INVALID_FILE;
	}

	//some special boxes (references and track groups) are handled by a single generic box with an associated ref/group type
	if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_TREF)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_TrackReferenceTypeBox*)newBox)->reference_type = type;
	} else if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_IREF)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_REFI);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_ItemReferenceTypeBox*)newBox)->reference_type = type;
	} else if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_TRGR)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_TRGT);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_TrackGroupTypeBox*)newBox)->group_type = type;
	} else if (parent_type && (parent_type == GF_ISOM_BOX_TYPE_GRPL)) {
		newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_GRPT);
		if (!newBox) return GF_OUT_OF_MEM;
		((GF_EntityToGroupTypeBox*)newBox)->grouping_type = type;
	} else {
		//OK, create the box based on the type
		is_special = GF_FALSE;
		newBox = gf_isom_box_new_ex(uuid_type ? uuid_type : type, parent_type, skip_logs, is_root_box);
		if (!newBox) return GF_OUT_OF_MEM;
	}

	//OK, init and read this box
	if (type==GF_ISOM_BOX_TYPE_UUID && !is_special) {
		memcpy(((GF_UUIDBox *)newBox)->uuid, uuid, 16);
		((GF_UUIDBox *)newBox)->internal_4cc = uuid_type;
	}

	if (!newBox->type) newBox->type = type;
	payload_start = gf_bs_get_position(bs);

retry_unknown_box:

	end = gf_bs_available(bs);
	if (size - hdr_size > end ) {
		newBox->size = size - hdr_size - end;
		*outBox = newBox;
		return GF_ISOM_INCOMPLETE_FILE;
	}

	newBox->size = size - hdr_size;

	//parse even if size is 0 - this makes sure that we perform box parsing (usually in box->read)
	e = gf_isom_full_box_read(newBox, bs);
	if (!e) e = gf_isom_box_read(newBox, bs);
	newBox->size = size;
	end = gf_bs_get_position(bs);

	if (e && (e != GF_ISOM_INCOMPLETE_FILE)) {
		gf_isom_box_del(newBox);
		*outBox = NULL;

		if (parent_type==GF_ISOM_BOX_TYPE_STSD) {
			newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_UNKNOWN);
			((GF_UnknownBox *)newBox)->original_4cc = type;
			newBox->size = size;
			gf_bs_seek(bs, payload_start);
			goto retry_unknown_box;
		}
		if (!skip_logs) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Read Box \"%s\" (start "LLU") failed (%s) - skipping\n", gf_4cc_to_str(type), start, gf_error_to_string(e)));
		}
		//we don't try to reparse known boxes that have been failing (too dangerous)
		return e;
	}

	if (end-start > size) {
		if (!skip_logs) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" size "LLU" (start "LLU") invalid (read "LLU")\n", gf_4cc_to_str(type), LLU_CAST size, start, LLU_CAST (end-start) ));
		}
		/*let's still try to load the file since no error was notified*/
		gf_bs_seek(bs, start+size);
	} else if (end-start < size) {
		u32 to_skip = (u32) (size-(end-start));
		if (!skip_logs) {
			if ((to_skip!=4) || gf_bs_peek_bits(bs, 32, 0)) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" (start "LLU") has %u extra bytes\n", gf_4cc_to_str(type), start, to_skip));
			}
		}
		gf_bs_skip_bytes(bs, to_skip);
	}
	*outBox = newBox;

	return e;
}


Refactored SNMP engine after vulnerabilities

snmp_ber_decode_string_len_buffer(unsigned char *buf, uint32_t *buff_len, const char **str, uint32_t *length)
{
  uint8_t type, i, length_bytes;

  buf = snmp_ber_decode_type(buf, buff_len, &type);

  if(buf == NULL || type != BER_DATA_TYPE_OCTET_STRING) {
    /*
     * Sanity check
     * Invalid type in buffer
     */
    return NULL;
  }

  if((*buf & 0x80) == 0) {
    *length = (uint32_t)*buf++;
    (*buff_len)--;
  } else {

    length_bytes = (uint8_t)(*buf++ & 0x7F);
    (*buff_len)--;
    if(length_bytes > 4) {
      /*
       * Sanity check
       * It will not fit in the uint32_t
       */
      return NULL;
    }

    *length = (uint32_t)*buf++;
    (*buff_len)--;
    for(i = 1; i < length_bytes; ++i) {
      *length <<= 8;
      *length |= *buf++;
      (*buff_len)--;
    }
  }

  *str = (const char *)buf;
  *buff_len -= *length;

  return buf + *length;
}

An out-of-bounds read in the SNMP stack in Contiki-NG 4.4 and earlier allows an attacker to cause a denial of service and potentially disclose information via crafted SNMP packets to snmp_ber_decode_string_len_buffer in os/net/app-layer/snmp/snmp-ber.c.


snmp_ber_decode_string_len_buffer(snmp_packet_t *snmp_packet, const char **str, uint32_t *length)
{
  uint8_t type, i, length_bytes;

  if(!snmp_ber_decode_type(snmp_packet, &type)) {
    return 0;
  }

  if(type != BER_DATA_TYPE_OCTET_STRING) {
    /*
     * Sanity check
     * Invalid type in buffer
     */
    return 0;
  }

  if((*snmp_packet->in & 0x80) == 0) {

    if(snmp_packet->used == 0) {
      return 0;
    }

    *length = (uint32_t)*snmp_packet->in++;
    snmp_packet->used--;
  } else {

    if(snmp_packet->used == 0) {
      return 0;
    }

    length_bytes = (uint8_t)(*snmp_packet->in++ & 0x7F);
    snmp_packet->used--;

    if(length_bytes > 4) {
      /*
       * Sanity check
       * It will not fit in the uint32_t
       */
      return 0;
    }

    if(snmp_packet->used == 0) {
      return 0;
    }

    *length = (uint32_t)*snmp_packet->in++;
    snmp_packet->used--;

    for(i = 1; i < length_bytes; ++i) {
      *length <<= 8;

      if(snmp_packet->used == 0) {
        return 0;
      }

      *length |= *snmp_packet->in++;
      snmp_packet->used--;
    }
  }

  *str = (const char *)snmp_packet->in;

  if(snmp_packet->used == 0 || snmp_packet->used - *length <= 0) {
    return 0;
  }

  snmp_packet->used -= *length;
  snmp_packet->in += *length;

  return 1;
}


Fix buffer overrun in SimpleParser::handleBackslash

Summary:
It read 4 chars, then checked for validity, but any of them could have
been the end of the string, so check after each one instead.

Reviewed By: oulgen

Differential Revision: D19611163

fbshipit-source-id: 3da0a39555cb85a93f4fd98048368f17cf37e2e4

  bool handleBackslash(signed char& out) {
    char ch = *p++;
    switch (ch) {
      case 0: return false;
      case '"': out = ch; return true;
      case '\\': out = ch; return true;
      case '/': out = ch; return true;
      case 'b': out = '\b'; return true;
      case 'f': out = '\f'; return true;
      case 'n': out = '\n'; return true;
      case 'r': out = '\r'; return true;
      case 't': out = '\t'; return true;
      case 'u': {
        if (UNLIKELY(is_tsimplejson)) {
          auto const ch1 = *p++;
          auto const ch2 = *p++;
          auto const dch3 = dehexchar(*p++);
          auto const dch4 = dehexchar(*p++);
          if (UNLIKELY(ch1 != '0' || ch2 != '0' || dch3 < 0 || dch4 < 0)) {
            return false;
          }
          out = (dch3 << 4) | dch4;
          return true;
        } else {
          uint16_t u16cp = 0;
          for (int i = 0; i < 4; i++) {
            auto const hexv = dehexchar(*p++);
            if (hexv < 0) return false; // includes check for end of string
            u16cp <<= 4;
            u16cp |= hexv;
          }
          if (u16cp > 0x7f) {
            return false;
          } else {
            out = u16cp;
            return true;
          }
        }
      }
      default: return false;
    }
  }

Insufficient boundary checks when decoding JSON in handleBackslash reads out of bounds memory, potentially leading to DOS. This issue affects HHVM 4.45.0, 4.44.0, 4.43.0, 4.42.0, 4.41.0, 4.40.0, 4.39.0, versions between 4.33.0 and 4.38.0 (inclusive), versions between 4.9.0 and 4.32.0 (inclusive), and versions prior to 4.8.7.


  bool handleBackslash(signed char& out) {
    char ch = *p++;
    switch (ch) {
      case 0: return false;
      case '"': out = ch; return true;
      case '\\': out = ch; return true;
      case '/': out = ch; return true;
      case 'b': out = '\b'; return true;
      case 'f': out = '\f'; return true;
      case 'n': out = '\n'; return true;
      case 'r': out = '\r'; return true;
      case 't': out = '\t'; return true;
      case 'u': {
        if (UNLIKELY(is_tsimplejson)) {
          auto const ch1 = *p++;
          if (UNLIKELY(ch1 != '0')) return false;
          auto const ch2 = *p++;
          if (UNLIKELY(ch2 != '0')) return false;
          auto const dch3 = dehexchar(*p++);
          if (UNLIKELY(dch3 < 0)) return false;
          auto const dch4 = dehexchar(*p++);
          if (UNLIKELY(dch4 < 0)) return false;
          out = (dch3 << 4) | dch4;
          return true;
        } else {
          uint16_t u16cp = 0;
          for (int i = 0; i < 4; i++) {
            auto const hexv = dehexchar(*p++);
            if (hexv < 0) return false; // includes check for end of string
            u16cp <<= 4;
            u16cp |= hexv;
          }
          if (u16cp > 0x7f) {
            return false;
          } else {
            out = u16cp;
            return true;
          }
        }
      }
      default: return false;
    }
  }


Limit number of connected sockets from unpaired devices

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.

void LanLinkProvider::addLink(const QString& deviceId, QSslSocket* socket, NetworkPacket* receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin)
{
    // Socket disconnection will now be handled by LanDeviceLink
    disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater);

    LanDeviceLink* deviceLink;
    //Do we have a link for this device already?
    QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(deviceId);
    if (linkIterator != m_links.end()) {
        //qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId;
        deviceLink = linkIterator.value();
        deviceLink->reset(socket, connectionOrigin);
    } else {
        deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin);
        connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed);
        m_links[deviceId] = deviceLink;
        if (m_pairingHandlers.contains(deviceId)) {
            //We shouldn't have a pairinghandler if we didn't have a link.
            //Crash if debug, recover if release (by setting the new devicelink to the old pairinghandler)
            Q_ASSERT(m_pairingHandlers.contains(deviceId));
            m_pairingHandlers[deviceId]->setDeviceLink(deviceLink);
        }
    }
    Q_EMIT onConnectionReceived(*receivedPacket, deviceLink);
}

In kdeconnect-kde (aka KDE Connect) before 20.08.2, an attacker on the local network could send crafted packets that trigger use of large amounts of CPU, memory, or network connection slots, aka a Denial of Service attack.