fetch_token(OnigToken* tok, UChar** src, UChar* end, ScanEnv* env)
{
  int r, num;
  OnigCodePoint c;
  OnigEncoding enc = env->enc;
  OnigSyntaxType* syn = env->syntax;
  UChar* prev;
  UChar* p = *src;
  PFETCH_READY;

 start:
  if (PEND) {
    tok->type = TK_EOT;
    return tok->type;
  }

  tok->type  = TK_STRING;
  tok->base  = 0;
  tok->backp = p;

  PFETCH(c);
  if (IS_MC_ESC_CODE(c, syn)) {
    if (PEND) return ONIGERR_END_PATTERN_AT_ESCAPE;

    tok->backp = p;
    PFETCH(c);

    tok->u.c = c;
    tok->escaped = 1;
    switch (c) {
    case '*':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_ASTERISK_ZERO_INF)) break;
      tok->type = TK_OP_REPEAT;
      tok->u.repeat.lower = 0;
      tok->u.repeat.upper = REPEAT_INFINITE;
      goto greedy_check;
      break;

    case '+':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_PLUS_ONE_INF)) break;
      tok->type = TK_OP_REPEAT;
      tok->u.repeat.lower = 1;
      tok->u.repeat.upper = REPEAT_INFINITE;
      goto greedy_check;
      break;

    case '?':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_QMARK_ZERO_ONE)) break;
      tok->type = TK_OP_REPEAT;
      tok->u.repeat.lower = 0;
      tok->u.repeat.upper = 1;
    greedy_check:
      if (!PEND && PPEEK_IS('?') &&
          IS_SYNTAX_OP(syn, ONIG_SYN_OP_QMARK_NON_GREEDY)) {
        PFETCH(c);
        tok->u.repeat.greedy     = 0;
        tok->u.repeat.possessive = 0;
      }
      else {
      possessive_check:
        if (!PEND && PPEEK_IS('+') &&
            ((IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_PLUS_POSSESSIVE_REPEAT) &&
              tok->type != TK_INTERVAL)  ||
             (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_PLUS_POSSESSIVE_INTERVAL) &&
              tok->type == TK_INTERVAL))) {
          PFETCH(c);
          tok->u.repeat.greedy     = 1;
          tok->u.repeat.possessive = 1;
        }
        else {
          tok->u.repeat.greedy     = 1;
          tok->u.repeat.possessive = 0;
        }
      }
      break;

    case '{':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_BRACE_INTERVAL)) break;
      r = fetch_range_quantifier(&p, end, tok, env);
      if (r < 0) return r;  /* error */
      if (r == 0) goto greedy_check;
      else if (r == 2) { /* {n} */
        if (IS_SYNTAX_BV(syn, ONIG_SYN_FIXED_INTERVAL_IS_GREEDY_ONLY))
          goto possessive_check;

        goto greedy_check;
      }
      /* r == 1 : normal char */
      break;

    case '|':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_VBAR_ALT)) break;
      tok->type = TK_ALT;
      break;

    case '(':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_LPAREN_SUBEXP)) break;
      tok->type = TK_SUBEXP_OPEN;
      break;

    case ')':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_LPAREN_SUBEXP)) break;
      tok->type = TK_SUBEXP_CLOSE;
      break;

    case 'w':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_W_WORD)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_WORD;
      tok->u.prop.not   = 0;
      break;

    case 'W':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_W_WORD)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_WORD;
      tok->u.prop.not   = 1;
      break;

    case 'b':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_B_WORD_BOUND)) break;
      tok->type = TK_ANCHOR;
      tok->u.anchor = ANCHOR_WORD_BOUNDARY;
      break;

    case 'B':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_B_WORD_BOUND)) break;
      tok->type = TK_ANCHOR;
      tok->u.anchor = ANCHOR_NO_WORD_BOUNDARY;
      break;

    case 'y':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP2_ESC_X_Y_GRAPHEME_CLUSTER)) break;
      tok->type = TK_ANCHOR;
      tok->u.anchor = ANCHOR_EXTENDED_GRAPHEME_CLUSTER_BOUNDARY;
      break;

    case 'Y':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP2_ESC_X_Y_GRAPHEME_CLUSTER)) break;
      tok->type = TK_ANCHOR;
      tok->u.anchor = ANCHOR_NO_EXTENDED_GRAPHEME_CLUSTER_BOUNDARY;
      break;

#ifdef USE_WORD_BEGIN_END
    case '<':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END)) break;
      tok->type = TK_ANCHOR;
      tok->u.anchor = ANCHOR_WORD_BEGIN;
      break;

    case '>':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END)) break;
      tok->type = TK_ANCHOR;
      tok->u.anchor = ANCHOR_WORD_END;
      break;
#endif

    case 's':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_S_WHITE_SPACE)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_SPACE;
      tok->u.prop.not   = 0;
      break;

    case 'S':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_S_WHITE_SPACE)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_SPACE;
      tok->u.prop.not   = 1;
      break;

    case 'd':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_D_DIGIT)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_DIGIT;
      tok->u.prop.not   = 0;
      break;

    case 'D':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_D_DIGIT)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_DIGIT;
      tok->u.prop.not   = 1;
      break;

    case 'h':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_H_XDIGIT)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_XDIGIT;
      tok->u.prop.not   = 0;
      break;

    case 'H':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_H_XDIGIT)) break;
      tok->type = TK_CHAR_TYPE;
      tok->u.prop.ctype = ONIGENC_CTYPE_XDIGIT;
      tok->u.prop.not   = 1;
      break;

    case 'K':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP)) break;
      tok->type = TK_KEEP;
      break;

    case 'R':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_CAPITAL_R_GENERAL_NEWLINE)) break;
      tok->type = TK_GENERAL_NEWLINE;
      break;

    case 'N':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_CAPITAL_N_O_SUPER_DOT)) break;
      tok->type = TK_NO_NEWLINE;
      break;

    case 'O':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_CAPITAL_N_O_SUPER_DOT)) break;
      tok->type = TK_TRUE_ANYCHAR;
      break;

    case 'X':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_X_Y_GRAPHEME_CLUSTER)) break;
      tok->type = TK_EXTENDED_GRAPHEME_CLUSTER;
      break;

    case 'A':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR)) break;
    begin_buf:
      tok->type = TK_ANCHOR;
      tok->u.subtype = ANCHOR_BEGIN_BUF;
      break;

    case 'Z':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR)) break;
      tok->type = TK_ANCHOR;
      tok->u.subtype = ANCHOR_SEMI_END_BUF;
      break;

    case 'z':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR)) break;
    end_buf:
      tok->type = TK_ANCHOR;
      tok->u.subtype = ANCHOR_END_BUF;
      break;

    case 'G':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_CAPITAL_G_BEGIN_ANCHOR)) break;
      tok->type = TK_ANCHOR;
      tok->u.subtype = ANCHOR_BEGIN_POSITION;
      break;

    case '`':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_GNU_BUF_ANCHOR)) break;
      goto begin_buf;
      break;

    case '\'':
      if (! IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_GNU_BUF_ANCHOR)) break;
      goto end_buf;
      break;

    case 'o':
      if (PEND) break;

      prev = p;
      if (PPEEK_IS('{') && IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_O_BRACE_OCTAL)) {
        PINC;
        num = scan_unsigned_octal_number(&p, end, 11, enc);
        if (num < 0) return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
        if (!PEND) {
          if (IS_CODE_DIGIT_ASCII(enc, PPEEK))
            return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
        }

        if ((p > prev + enclen(enc, prev)) && !PEND && PPEEK_IS('}')) {
          PINC;
          tok->type   = TK_CODE_POINT;
          tok->u.code = (OnigCodePoint )num;
        }
        else {
          /* can't read nothing or invalid format */
          p = prev;
        }
      }
      break;

    case 'x':
      if (PEND) break;

      prev = p;
      if (PPEEK_IS('{') && IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_X_BRACE_HEX8)) {
        PINC;
        num = scan_unsigned_hexadecimal_number(&p, end, 0, 8, enc);
        if (num < 0) {
          if (num == ONIGERR_TOO_BIG_NUMBER)
            return ONIGERR_TOO_BIG_WIDE_CHAR_VALUE;
          else
            return num;
        }
        if (!PEND) {
          if (IS_CODE_XDIGIT_ASCII(enc, PPEEK))
            return ONIGERR_TOO_LONG_WIDE_CHAR_VALUE;
        }

        if ((p > prev + enclen(enc, prev)) && !PEND && PPEEK_IS('}')) {
          PINC;
          tok->type   = TK_CODE_POINT;
          tok->u.code = (OnigCodePoint )num;
        }
        else {
          /* can't read nothing or invalid format */
          p = prev;
        }
      }
      else if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_X_HEX2)) {
        num = scan_unsigned_hexadecimal_number(&p, end, 0, 2, enc);
        if (num < 0) return num;
        if (p == prev) {  /* can't read nothing. */
          num = 0; /* but, it's not error */
        }
        tok->type = TK_RAW_BYTE;
        tok->base = 16;
        tok->u.c  = num;
      }
      break;

    case 'u':
      if (PEND) break;

      prev = p;
      if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_U_HEX4)) {
        num = scan_unsigned_hexadecimal_number(&p, end, 4, 4, enc);
        if (num < 0) return num;
        if (p == prev) {  /* can't read nothing. */
          num = 0; /* but, it's not error */
        }
        tok->type   = TK_CODE_POINT;
        tok->base   = 16;
        tok->u.code = (OnigCodePoint )num;
      }
      break;

    case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
      PUNFETCH;
      prev = p;
      num = onig_scan_unsigned_number(&p, end, enc);
      if (num < 0 || num > ONIG_MAX_BACKREF_NUM) {
        goto skip_backref;
      }

      if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_DECIMAL_BACKREF) &&
          (num <= env->num_mem || num <= 9)) { /* This spec. from GNU regex */
        if (IS_SYNTAX_BV(syn, ONIG_SYN_STRICT_CHECK_BACKREF)) {
          if (num > env->num_mem || IS_NULL(SCANENV_MEMENV(env)[num].node))
            return ONIGERR_INVALID_BACKREF;
        }

        tok->type = TK_BACKREF;
        tok->u.backref.num     = 1;
        tok->u.backref.ref1    = num;
        tok->u.backref.by_name = 0;
#ifdef USE_BACKREF_WITH_LEVEL
        tok->u.backref.exist_level = 0;
#endif
        break;
      }

    skip_backref:
      if (c == '8' || c == '9') {
        /* normal char */
        p = prev; PINC;
        break;
      }

      p = prev;
      /* fall through */
    case '0':
      if (IS_SYNTAX_OP(syn, ONIG_SYN_OP_ESC_OCTAL3)) {
        prev = p;
        num = scan_unsigned_octal_number(&p, end, (c == '0' ? 2:3), enc);
        if (num < 0 || num >= 256) return ONIGERR_TOO_BIG_NUMBER;
        if (p == prev) {  /* can't read nothing. */
          num = 0; /* but, it's not error */
        }
        tok->type = TK_RAW_BYTE;
        tok->base = 8;
        tok->u.c  = num;
      }
      else if (c != '0') {
        PINC;
      }
      break;

    case 'k':
      if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_K_NAMED_BACKREF)) {
        PFETCH(c);
        if (c == '<' || c == '\'') {
          UChar* name_end;
          int* backs;
          int back_num;
          enum REF_NUM num_type;

          prev = p;

#ifdef USE_BACKREF_WITH_LEVEL
          name_end = NULL_UCHARP; /* no need. escape gcc warning. */
          r = fetch_name_with_level((OnigCodePoint )c, &p, end, &name_end,
                                 env, &back_num, &tok->u.backref.level, &num_type);
          if (r == 1) tok->u.backref.exist_level = 1;
          else        tok->u.backref.exist_level = 0;
#else
          r = fetch_name(c, &p, end, &name_end, env, &back_num, &num_type, 1);
#endif
          if (r < 0) return r;

          if (num_type != IS_NOT_NUM) {
            if (num_type == IS_REL_NUM) {
              back_num = backref_rel_to_abs(back_num, env);
            }
            if (back_num <= 0)
              return ONIGERR_INVALID_BACKREF;

            if (IS_SYNTAX_BV(syn, ONIG_SYN_STRICT_CHECK_BACKREF)) {
              if (back_num > env->num_mem ||
                  IS_NULL(SCANENV_MEMENV(env)[back_num].node))
                return ONIGERR_INVALID_BACKREF;
            }
            tok->type = TK_BACKREF;
            tok->u.backref.by_name = 0;
            tok->u.backref.num  = 1;
            tok->u.backref.ref1 = back_num;
          }
          else {
            num = onig_name_to_group_numbers(env->reg, prev, name_end, &backs);
            if (num <= 0) {
              onig_scan_env_set_error_string(env,
                        ONIGERR_UNDEFINED_NAME_REFERENCE, prev, name_end);
              return ONIGERR_UNDEFINED_NAME_REFERENCE;
            }
            if (IS_SYNTAX_BV(syn, ONIG_SYN_STRICT_CHECK_BACKREF)) {
              int i;
              for (i = 0; i < num; i++) {
                if (backs[i] > env->num_mem ||
                    IS_NULL(SCANENV_MEMENV(env)[backs[i]].node))
                  return ONIGERR_INVALID_BACKREF;
              }
            }

            tok->type = TK_BACKREF;
            tok->u.backref.by_name = 1;
            if (num == 1) {
              tok->u.backref.num  = 1;
              tok->u.backref.ref1 = backs[0];
            }
            else {
              tok->u.backref.num  = num;
              tok->u.backref.refs = backs;
            }
          }
        }
        else
          PUNFETCH;
      }
      break;

#ifdef USE_CALL
    case 'g':
      if (!PEND && IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_G_SUBEXP_CALL)) {
        PFETCH(c);
        if (c == '<' || c == '\'') {
          int gnum;
          UChar* name_end;
          enum REF_NUM num_type;

          prev = p;
          r = fetch_name((OnigCodePoint )c, &p, end, &name_end, env,
                         &gnum, &num_type, 1);
          if (r < 0) return r;

          if (num_type != IS_NOT_NUM) {
            if (num_type == IS_REL_NUM) {
              gnum = backref_rel_to_abs(gnum, env);
              if (gnum < 0) {
                onig_scan_env_set_error_string(env, ONIGERR_UNDEFINED_NAME_REFERENCE,
                                               prev, name_end);
                return ONIGERR_UNDEFINED_GROUP_REFERENCE;
              }
            }
            tok->u.call.by_number = 1;
            tok->u.call.gnum      = gnum;
          }
          else {
            tok->u.call.by_number = 0;
            tok->u.call.gnum      = 0;
          }

          tok->type = TK_CALL;
          tok->u.call.name     = prev;
          tok->u.call.name_end = name_end;
        }
        else
          PUNFETCH;
      }
      break;
#endif

    case 'Q':
      if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_CAPITAL_Q_QUOTE)) {
        tok->type = TK_QUOTE_OPEN;
      }
      break;

    case 'p':
    case 'P':
      if (!PEND && PPEEK_IS('{') &&
          IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY)) {
        PINC;
        tok->type = TK_CHAR_PROPERTY;
        tok->u.prop.not = (c == 'P' ? 1 : 0);

        if (!PEND &&
            IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT)) {
          PFETCH(c);
          if (c == '^') {
            tok->u.prop.not = (tok->u.prop.not == 0 ? 1 : 0);
          }
          else
            PUNFETCH;
        }
      }
      break;

    default:
      {
        OnigCodePoint c2;

        PUNFETCH;
        num = fetch_escaped_value(&p, end, env, &c2);
        if (num < 0) return num;
        /* set_raw: */
        if (tok->u.c != c2) {
          tok->type = TK_CODE_POINT;
          tok->u.code = c2;
        }
        else { /* string */
          p = tok->backp + enclen(enc, tok->backp);
        }
      }
      break;
    }
  }
  else {
    tok->u.c = c;
    tok->escaped = 0;

#ifdef USE_VARIABLE_META_CHARS
    if ((c != ONIG_INEFFECTIVE_META_CHAR) &&
        IS_SYNTAX_OP(syn, ONIG_SYN_OP_VARIABLE_META_CHARACTERS)) {
      if (c == MC_ANYCHAR(syn))
        goto any_char;
      else if (c == MC_ANYTIME(syn))
        goto anytime;
      else if (c == MC_ZERO_OR_ONE_TIME(syn))
        goto zero_or_one_time;
      else if (c == MC_ONE_OR_MORE_TIME(syn))
        goto one_or_more_time;
      else if (c == MC_ANYCHAR_ANYTIME(syn)) {
        tok->type = TK_ANYCHAR_ANYTIME;
        goto out;
      }
    }
#endif

    switch (c) {
    case '.':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_DOT_ANYCHAR)) break;
#ifdef USE_VARIABLE_META_CHARS
    any_char:
#endif
      tok->type = TK_ANYCHAR;
      break;

    case '*':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_ASTERISK_ZERO_INF)) break;
#ifdef USE_VARIABLE_META_CHARS
    anytime:
#endif
      tok->type = TK_OP_REPEAT;
      tok->u.repeat.lower = 0;
      tok->u.repeat.upper = REPEAT_INFINITE;
      goto greedy_check;
      break;

    case '+':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_PLUS_ONE_INF)) break;
#ifdef USE_VARIABLE_META_CHARS
    one_or_more_time:
#endif
      tok->type = TK_OP_REPEAT;
      tok->u.repeat.lower = 1;
      tok->u.repeat.upper = REPEAT_INFINITE;
      goto greedy_check;
      break;

    case '?':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_QMARK_ZERO_ONE)) break;
#ifdef USE_VARIABLE_META_CHARS
    zero_or_one_time:
#endif
      tok->type = TK_OP_REPEAT;
      tok->u.repeat.lower = 0;
      tok->u.repeat.upper = 1;
      goto greedy_check;
      break;

    case '{':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_BRACE_INTERVAL)) break;
      r = fetch_range_quantifier(&p, end, tok, env);
      if (r < 0) return r;  /* error */
      if (r == 0) goto greedy_check;
      else if (r == 2) { /* {n} */
        if (IS_SYNTAX_BV(syn, ONIG_SYN_FIXED_INTERVAL_IS_GREEDY_ONLY))
          goto possessive_check;

        goto greedy_check;
      }
      /* r == 1 : normal char */
      break;

    case '|':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_VBAR_ALT)) break;
      tok->type = TK_ALT;
      break;

    case '(':
      if (!PEND && PPEEK_IS('?') &&
          IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_QMARK_GROUP_EFFECT)) {
        PINC;
        if (! PEND) {
          c = PPEEK;
          if (c == '#') {
            PFETCH(c);
            while (1) {
              if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
              PFETCH(c);
              if (c == MC_ESC(syn)) {
                if (! PEND) PFETCH(c);
              }
              else {
                if (c == ')') break;
              }
            }
            goto start;
          }
          else if (IS_SYNTAX_OP2(syn, ONIG_SYN_OP2_QMARK_PERL_SUBEXP_CALL)) {
            int gnum;
            UChar* name;
            UChar* name_end;
            enum REF_NUM num_type;

            switch (c) {
            case '&':
              {
                PINC;
                name = p;
                r = fetch_name((OnigCodePoint )'(', &p, end, &name_end, env, &gnum,
                               &num_type, 0);
                if (r < 0) return r;

                tok->type = TK_CALL;
                tok->u.call.by_number = 0;
                tok->u.call.gnum      = 0;
                tok->u.call.name      = name;
                tok->u.call.name_end  = name_end;
              }
              break;

            case 'R':
              tok->type = TK_CALL;
              tok->u.call.by_number = 1;
              tok->u.call.gnum      = 0;
              tok->u.call.name      = p;
              PINC;
              if (! PPEEK_IS(')')) return ONIGERR_INVALID_GROUP_NAME;
              tok->u.call.name_end  = p;
              break;

            case '-':
            case '+':
              goto lparen_qmark_num;
              break;
            default:
              if (! ONIGENC_IS_CODE_DIGIT(enc, c)) goto lparen_qmark_end;

            lparen_qmark_num:
              {
                name = p;
                r = fetch_name((OnigCodePoint )'(', &p, end, &name_end, env,
                               &gnum, &num_type, 1);
                if (r < 0) return r;

                if (num_type == IS_NOT_NUM) {
                  return ONIGERR_INVALID_GROUP_NAME;
                }
                else {
                  if (num_type == IS_REL_NUM) {
                    gnum = backref_rel_to_abs(gnum, env);
                    if (gnum < 0) {
                      onig_scan_env_set_error_string(env,
                             ONIGERR_UNDEFINED_NAME_REFERENCE, name, name_end);
                      return ONIGERR_UNDEFINED_GROUP_REFERENCE;
                    }
                  }
                  tok->u.call.by_number = 1;
                  tok->u.call.gnum      = gnum;
                }

                tok->type = TK_CALL;
                tok->u.call.name     = name;
                tok->u.call.name_end = name_end;
              }
              break;
            }
          }
        }
      lparen_qmark_end:
        PUNFETCH;
      }

      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_LPAREN_SUBEXP)) break;
      tok->type = TK_SUBEXP_OPEN;
      break;

    case ')':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_LPAREN_SUBEXP)) break;
      tok->type = TK_SUBEXP_CLOSE;
      break;

    case '^':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_LINE_ANCHOR)) break;
      tok->type = TK_ANCHOR;
      tok->u.subtype = (IS_SINGLELINE(env->options)
                        ? ANCHOR_BEGIN_BUF : ANCHOR_BEGIN_LINE);
      break;

    case '$':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_LINE_ANCHOR)) break;
      tok->type = TK_ANCHOR;
      tok->u.subtype = (IS_SINGLELINE(env->options)
                        ? ANCHOR_SEMI_END_BUF : ANCHOR_END_LINE);
      break;

    case '[':
      if (! IS_SYNTAX_OP(syn, ONIG_SYN_OP_BRACKET_CC)) break;
      tok->type = TK_CC_OPEN;
      break;

    case ']':
      if (*src > env->pattern)   /* /].../ is allowed. */
        CLOSE_BRACKET_WITHOUT_ESC_WARN(env, (UChar* )"]");
      break;

    case '#':
      if (IS_EXTEND(env->options)) {
        while (!PEND) {
          PFETCH(c);
          if (ONIGENC_IS_CODE_NEWLINE(enc, c))
            break;
        }
        goto start;
        break;
      }
      break;

    case ' ': case '\t': case '\n': case '\r': case '\f':
      if (IS_EXTEND(env->options))
        goto start;
      break;

    default:
      /* string */
      break;
    }
  }

#ifdef USE_VARIABLE_META_CHARS
 out:
#endif
  *src = p;
  return tok->type;
}


Bluetooth: hidp: buffer overflow in hidp_process_report

CVE-2018-9363

The buffer length is unsigned at all layers, but gets cast to int and
checked in hidp_process_report and can lead to a buffer overflow.
Switch len parameter to unsigned int to resolve issue.

This affects 3.18 and newer kernels.

Signed-off-by: Mark Salyzyn <salyzyn@android.com>
Fixes: a4b1b5877b514b276f0f31efe02388a9c2836728 ("HID: Bluetooth: hidp: make sure input buffers are big enough")
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Kees Cook <keescook@chromium.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: linux-bluetooth@vger.kernel.org
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: security@kernel.org
Cc: kernel-team@android.com
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

static void hidp_process_report(struct hidp_session *session,
				int type, const u8 *data, int len, int intr)
{
	if (len > HID_MAX_BUFFER_SIZE)
		len = HID_MAX_BUFFER_SIZE;

	memcpy(session->input_buf, data, len);
	hid_input_report(session->hid, type, session->input_buf, len, intr);
}

In the hidp_process_report in bluetooth, there is an integer overflow. This could lead to an out of bounds write with no additional execution privileges needed. User interaction is not needed for exploitation. Product: Android Versions: Android kernel Android ID: A-65853588 References: Upstream kernel.


static void hidp_process_report(struct hidp_session *session, int type,
				const u8 *data, unsigned int len, int intr)
{
	if (len > HID_MAX_BUFFER_SIZE)
		len = HID_MAX_BUFFER_SIZE;

	memcpy(session->input_buf, data, len);
	hid_input_report(session->hid, type, session->input_buf, len, intr);
}


(#6455) Fix issue validating VOMS proxies

extract_VOMS_info( globus_gsi_cred_handle_t cred_handle, int verify_type, char **voname, char **firstfqan, char **quoted_DN_and_FQAN)
{

#if !defined(HAVE_EXT_VOMS)
	return 1;
#else

	int ret;
	struct vomsdata *voms_data = NULL;
	struct voms *voms_cert  = NULL;
	char *subject_name = NULL;
	char **fqan = NULL;
	int voms_err;
	int fqan_len = 0;
	char *retfqan = NULL;
	char *tmp_scan_ptr = NULL;

	STACK_OF(X509) *chain = NULL;
	X509 *cert = NULL;

	char* x509_fqan_delimiter = NULL;

	if ( activate_globus_gsi() != 0 ) {
		return 1;
	}

	// calling this function on something that doesn't have VOMS attributes
	// should return error 1.  when the config knob disables VOMS, behave the
	// same way.
	if (!param_boolean_int("USE_VOMS_ATTRIBUTES", 1)) {
		return 1;
	}

	ret = (*globus_gsi_cred_get_cert_chain_ptr)(cred_handle, &chain);
	if(ret != GLOBUS_SUCCESS) {
		ret = 10;
		goto end;
	}

	ret = (*globus_gsi_cred_get_cert_ptr)(cred_handle, &cert);
	if(ret != GLOBUS_SUCCESS) {
		ret = 11;
		goto end;
	}

	if ((*globus_gsi_cred_get_identity_name_ptr)(cred_handle, &subject_name)) {
		set_error_string( "unable to extract subject name" );
		ret = 12;
		goto end;
	}

	voms_data = (*VOMS_Init_ptr)(NULL, NULL);
	if (voms_data == NULL) {
		ret = 13;
		goto end;
	}

	if (verify_type == 0) {
		ret = (*VOMS_SetVerificationType_ptr)( VERIFY_NONE, voms_data, &voms_err );
		if (ret == 0) {
			(*VOMS_ErrorMessage_ptr)(voms_data, voms_err, NULL, 0);
			ret = voms_err;
			goto end;
		}
	}

	ret = (*VOMS_Retrieve_ptr)(cert, chain, RECURSE_CHAIN,
						voms_data, &voms_err);
	if (ret == 0) {
		if (voms_err == VERR_NOEXT) {
			// No VOMS extensions present
			ret = 1;
			goto end;
		} else {
			(*VOMS_ErrorMessage_ptr)(voms_data, voms_err, NULL, 0);
			ret = voms_err;
			goto end;
		}
	}

	// we only support one cert for now.  serializing and encoding all the
	// attributes is bad enough, i don't want to deal with doing this to
	// multiple certs.
	voms_cert = voms_data->data[0];

	// fill in the unquoted versions of things
	if(voname) {
		*voname = strdup(voms_cert->voname);
	}

	if(firstfqan) {
		*firstfqan = strdup(voms_cert->fqan[0]);
	}

	// only construct the quoted_DN_and_FQAN if needed
	if (quoted_DN_and_FQAN) {
		// get our delimiter and trim it
		if (!(x509_fqan_delimiter = param("X509_FQAN_DELIMITER"))) {
			x509_fqan_delimiter = strdup(",");
		}
		tmp_scan_ptr = trim_quotes(x509_fqan_delimiter);
		free(x509_fqan_delimiter);
		x509_fqan_delimiter = tmp_scan_ptr;

		// calculate the length
		fqan_len = 0;

		// start with the length of the quoted DN
		tmp_scan_ptr = quote_x509_string( subject_name );
		fqan_len += strlen( tmp_scan_ptr );
		free(tmp_scan_ptr);

		// add the length of delimiter plus each voms attribute
		for (fqan = voms_cert->fqan; fqan && *fqan; fqan++) {
			// delimiter
			fqan_len += strlen(x509_fqan_delimiter);

			tmp_scan_ptr = quote_x509_string( *fqan );
			fqan_len += strlen( tmp_scan_ptr );
			free(tmp_scan_ptr);
		}

		// now malloc enough room for the quoted DN, quoted attrs, delimiters, and
		// NULL terminator
		retfqan = (char*) malloc (fqan_len+1);
		*retfqan = 0;  // set null terminiator

		// reset length counter -- we use this for efficient appending.
		fqan_len = 0;

		// start with the quoted DN
		tmp_scan_ptr = quote_x509_string( subject_name );
		strcat(retfqan, tmp_scan_ptr);
		fqan_len += strlen( tmp_scan_ptr );
		free(tmp_scan_ptr);

		// add the delimiter plus each voms attribute
		for (fqan = voms_cert->fqan; fqan && *fqan; fqan++) {
			// delimiter
			strcat(&(retfqan[fqan_len]), x509_fqan_delimiter);
			fqan_len += strlen(x509_fqan_delimiter);

			tmp_scan_ptr = quote_x509_string( *fqan );
			strcat(&(retfqan[fqan_len]), tmp_scan_ptr);
			fqan_len += strlen( tmp_scan_ptr );
			free(tmp_scan_ptr);
		}

		*quoted_DN_and_FQAN = retfqan;
	}

	ret = 0;

end:
	free(subject_name);
	free(x509_fqan_delimiter);
	if (voms_data)
		(*VOMS_Destroy_ptr)(voms_data);
	if (cert)
		X509_free(cert);
	if(chain)
		sk_X509_pop_free(chain, X509_free);

	return ret;
#endif

}

The condor_schedd component in HTCondor before 8.6.8 and 8.7.x before 8.7.5 allows remote authenticated users to cause a denial of service (daemon crash) by leveraging use of GSI and VOMS extensions.


extract_VOMS_info( globus_gsi_cred_handle_t cred_handle, int verify_type, char **voname, char **firstfqan, char **quoted_DN_and_FQAN)
{

#if !defined(HAVE_EXT_VOMS)
	return 1;
#else

	int ret;
	struct vomsdata *voms_data = NULL;
	struct voms *voms_cert  = NULL;
	char *subject_name = NULL;
	char **fqan = NULL;
	int voms_err;
	int fqan_len = 0;
	char *retfqan = NULL;
	char *tmp_scan_ptr = NULL;

	STACK_OF(X509) *chain = NULL;
	X509 *cert = NULL;

	char* x509_fqan_delimiter = NULL;

	if ( activate_globus_gsi() != 0 ) {
		return 1;
	}

	// calling this function on something that doesn't have VOMS attributes
	// should return error 1.  when the config knob disables VOMS, behave the
	// same way.
	if (!param_boolean_int("USE_VOMS_ATTRIBUTES", 1)) {
		return 1;
	}

	ret = (*globus_gsi_cred_get_cert_chain_ptr)(cred_handle, &chain);
	if(ret != GLOBUS_SUCCESS) {
		ret = 10;
		goto end;
	}

	ret = (*globus_gsi_cred_get_cert_ptr)(cred_handle, &cert);
	if(ret != GLOBUS_SUCCESS) {
		ret = 11;
		goto end;
	}

	if ((*globus_gsi_cred_get_identity_name_ptr)(cred_handle, &subject_name)) {
		set_error_string( "unable to extract subject name" );
		ret = 12;
		goto end;
	}

	voms_data = (*VOMS_Init_ptr)(NULL, NULL);
	if (voms_data == NULL) {
		ret = 13;
		goto end;
	}

	if (verify_type == 0) {
		ret = (*VOMS_SetVerificationType_ptr)( VERIFY_NONE, voms_data, &voms_err );
		if (ret == 0) {
			(*VOMS_ErrorMessage_ptr)(voms_data, voms_err, NULL, 0);
			ret = voms_err;
			goto end;
		}
	}

	ret = (*VOMS_Retrieve_ptr)(cert, chain, RECURSE_CHAIN,
						voms_data, &voms_err);
	if (ret == 0) {
		if (voms_err == VERR_NOEXT) {
			// No VOMS extensions present
			ret = 1;
			goto end;
		} else {
			(*VOMS_ErrorMessage_ptr)(voms_data, voms_err, NULL, 0);
			ret = voms_err;
			goto end;
		}
	}

	// we only support one cert for now.  serializing and encoding all the
	// attributes is bad enough, i don't want to deal with doing this to
	// multiple certs.
	voms_cert = voms_data->data[0];

	if (voms_cert == NULL) {
		// No VOMS certs?? Treat like VOMS_Retrieve() returned VERR_NOEXT.
		ret = 1;
		goto end;
	}

	// fill in the unquoted versions of things
	if(voname) {
		*voname = strdup(voms_cert->voname ? voms_cert->voname : "");
	}

	if(firstfqan) {
		*firstfqan = strdup(voms_cert->fqan[0] ? voms_cert->fqan[0] : "");
	}

	// only construct the quoted_DN_and_FQAN if needed
	if (quoted_DN_and_FQAN) {
		// get our delimiter and trim it
		if (!(x509_fqan_delimiter = param("X509_FQAN_DELIMITER"))) {
			x509_fqan_delimiter = strdup(",");
		}
		tmp_scan_ptr = trim_quotes(x509_fqan_delimiter);
		free(x509_fqan_delimiter);
		x509_fqan_delimiter = tmp_scan_ptr;

		// calculate the length
		fqan_len = 0;

		// start with the length of the quoted DN
		tmp_scan_ptr = quote_x509_string( subject_name );
		fqan_len += strlen( tmp_scan_ptr );
		free(tmp_scan_ptr);

		// add the length of delimiter plus each voms attribute
		for (fqan = voms_cert->fqan; fqan && *fqan; fqan++) {
			// delimiter
			fqan_len += strlen(x509_fqan_delimiter);

			tmp_scan_ptr = quote_x509_string( *fqan );
			fqan_len += strlen( tmp_scan_ptr );
			free(tmp_scan_ptr);
		}

		// now malloc enough room for the quoted DN, quoted attrs, delimiters, and
		// NULL terminator
		retfqan = (char*) malloc (fqan_len+1);
		*retfqan = 0;  // set null terminiator

		// reset length counter -- we use this for efficient appending.
		fqan_len = 0;

		// start with the quoted DN
		tmp_scan_ptr = quote_x509_string( subject_name );
		strcat(retfqan, tmp_scan_ptr);
		fqan_len += strlen( tmp_scan_ptr );
		free(tmp_scan_ptr);

		// add the delimiter plus each voms attribute
		for (fqan = voms_cert->fqan; fqan && *fqan; fqan++) {
			// delimiter
			strcat(&(retfqan[fqan_len]), x509_fqan_delimiter);
			fqan_len += strlen(x509_fqan_delimiter);

			tmp_scan_ptr = quote_x509_string( *fqan );
			strcat(&(retfqan[fqan_len]), tmp_scan_ptr);
			fqan_len += strlen( tmp_scan_ptr );
			free(tmp_scan_ptr);
		}

		*quoted_DN_and_FQAN = retfqan;
	}

	ret = 0;

end:
	free(subject_name);
	free(x509_fqan_delimiter);
	if (voms_data)
		(*VOMS_Destroy_ptr)(voms_data);
	if (cert)
		X509_free(cert);
	if(chain)
		sk_X509_pop_free(chain, X509_free);

	return ret;
#endif

}


fix regression in "epoll: Keep a reference on files added to the check list"

epoll_loop_check_proc() can run into a file already committed to destruction;
we can't grab a reference on those and don't need to add them to the set for
reverse path check anyway.

Tested-by: Marc Zyngier <maz@kernel.org>
Fixes: a9ed4a6560b8 ("epoll: Keep a reference on files added to the check list")
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

static int ep_loop_check_proc(void *priv, void *cookie, int call_nests)
{
	int error = 0;
	struct file *file = priv;
	struct eventpoll *ep = file->private_data;
	struct eventpoll *ep_tovisit;
	struct rb_node *rbp;
	struct epitem *epi;

	mutex_lock_nested(&ep->mtx, call_nests + 1);
	ep->visited = 1;
	list_add(&ep->visited_list_link, &visited_list);
	for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = rb_next(rbp)) {
		epi = rb_entry(rbp, struct epitem, rbn);
		if (unlikely(is_file_epoll(epi->ffd.file))) {
			ep_tovisit = epi->ffd.file->private_data;
			if (ep_tovisit->visited)
				continue;
			error = ep_call_nested(&poll_loop_ncalls,
					ep_loop_check_proc, epi->ffd.file,
					ep_tovisit, current);
			if (error != 0)
				break;
		} else {
			/*
			 * If we've reached a file that is not associated with
			 * an ep, then we need to check if the newly added
			 * links are going to add too many wakeup paths. We do
			 * this by adding it to the tfile_check_list, if it's
			 * not already there, and calling reverse_path_check()
			 * during ep_insert().
			 */
			if (list_empty(&epi->ffd.file->f_tfile_llink)) {
				get_file(epi->ffd.file);
				list_add(&epi->ffd.file->f_tfile_llink,
					 &tfile_check_list);
			}
		}
	}
	mutex_unlock(&ep->mtx);

	return error;
}

In ep_loop_check_proc of eventpoll.c, there is a possible way to corrupt memory due to a use after free. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.Product: AndroidVersions: Android kernelAndroid ID: A-204573007References: Upstream kernel


static int ep_loop_check_proc(void *priv, void *cookie, int call_nests)
{
	int error = 0;
	struct file *file = priv;
	struct eventpoll *ep = file->private_data;
	struct eventpoll *ep_tovisit;
	struct rb_node *rbp;
	struct epitem *epi;

	mutex_lock_nested(&ep->mtx, call_nests + 1);
	ep->visited = 1;
	list_add(&ep->visited_list_link, &visited_list);
	for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = rb_next(rbp)) {
		epi = rb_entry(rbp, struct epitem, rbn);
		if (unlikely(is_file_epoll(epi->ffd.file))) {
			ep_tovisit = epi->ffd.file->private_data;
			if (ep_tovisit->visited)
				continue;
			error = ep_call_nested(&poll_loop_ncalls,
					ep_loop_check_proc, epi->ffd.file,
					ep_tovisit, current);
			if (error != 0)
				break;
		} else {
			/*
			 * If we've reached a file that is not associated with
			 * an ep, then we need to check if the newly added
			 * links are going to add too many wakeup paths. We do
			 * this by adding it to the tfile_check_list, if it's
			 * not already there, and calling reverse_path_check()
			 * during ep_insert().
			 */
			if (list_empty(&epi->ffd.file->f_tfile_llink)) {
				if (get_file_rcu(epi->ffd.file))
					list_add(&epi->ffd.file->f_tfile_llink,
						 &tfile_check_list);
			}
		}
	}
	mutex_unlock(&ep->mtx);

	return error;
}


Avoid negative integer overflow when `filesize < io_->tell()`.

This fixes #791.

    void WebPImage::decodeChunks(uint64_t filesize)
    {
        DataBuf   chunkId(5);
        byte      size_buff[WEBP_TAG_SIZE];
        bool      has_canvas_data = false;

#ifdef DEBUG
        std::cout << "Reading metadata" << std::endl;
#endif

        chunkId.pData_[4] = '\0' ;
        while ( !io_->eof() && (uint64_t) io_->tell() < filesize) {
            io_->read(chunkId.pData_, WEBP_TAG_SIZE);
            io_->read(size_buff, WEBP_TAG_SIZE);
            const uint32_t size = Exiv2::getULong(size_buff, littleEndian);
            enforce(size <= (filesize - io_->tell()), Exiv2::kerCorruptedMetadata);

            DataBuf payload(size);

            if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8X) && !has_canvas_data) {
                enforce(size >= 10, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                byte size_buf[WEBP_TAG_SIZE];

                io_->read(payload.pData_, payload.size_);

                // Fetch width
                memcpy(&size_buf, &payload.pData_[4], 3);
                size_buf[3] = 0;
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1;

                // Fetch height
                memcpy(&size_buf, &payload.pData_[7], 3);
                size_buf[3] = 0;
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8) && !has_canvas_data) {
                enforce(size >= 10, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                io_->read(payload.pData_, payload.size_);
                byte size_buf[WEBP_TAG_SIZE];

                // Fetch width""
                memcpy(&size_buf, &payload.pData_[6], 2);
                size_buf[2] = 0;
                size_buf[3] = 0;
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff;

                // Fetch height
                memcpy(&size_buf, &payload.pData_[8], 2);
                size_buf[2] = 0;
                size_buf[3] = 0;
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8L) && !has_canvas_data) {
                enforce(size >= 5, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                byte size_buf_w[2];
                byte size_buf_h[3];

                io_->read(payload.pData_, payload.size_);

                // Fetch width
                memcpy(&size_buf_w, &payload.pData_[1], 2);
                size_buf_w[1] &= 0x3F;
                pixelWidth_ = Exiv2::getUShort(size_buf_w, littleEndian) + 1;

                // Fetch height
                memcpy(&size_buf_h, &payload.pData_[2], 3);
                size_buf_h[0] = ((size_buf_h[0] >> 6) & 0x3) | ((size_buf_h[1]  & 0x3F) << 0x2);
                size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0xF) << 0x2);
                pixelHeight_ = Exiv2::getUShort(size_buf_h, littleEndian) + 1;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_ANMF) && !has_canvas_data) {
                enforce(size >= 12, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                byte size_buf[WEBP_TAG_SIZE];

                io_->read(payload.pData_, payload.size_);

                // Fetch width
                memcpy(&size_buf, &payload.pData_[6], 3);
                size_buf[3] = 0;
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1;

                // Fetch height
                memcpy(&size_buf, &payload.pData_[9], 3);
                size_buf[3] = 0;
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_ICCP)) {
                io_->read(payload.pData_, payload.size_);
                this->setIccProfile(payload);
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_EXIF)) {
                io_->read(payload.pData_, payload.size_);

                byte  size_buff[2];
                // 4 meaningful bytes + 2 padding bytes
                byte  exifLongHeader[]   = { 0xFF, 0x01, 0xFF, 0xE1, 0x00, 0x00 };
                byte  exifShortHeader[]  = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
                byte  exifTiffLEHeader[] = { 0x49, 0x49, 0x2A };       // "MM*"
                byte  exifTiffBEHeader[] = { 0x4D, 0x4D, 0x00, 0x2A }; // "II\0*"
                byte* rawExifData = NULL;
                long  offset = 0;
                bool  s_header = false;
                bool  le_header = false;
                bool  be_header = false;
                long  pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifLongHeader, 4);

                if (pos == -1) {
                    pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifLongHeader, 6);
                    if (pos != -1) {
                        s_header = true;
                    }
                }
                if (pos == -1) {
                    pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifTiffLEHeader, 3);
                    if (pos != -1) {
                        le_header = true;
                    }
                }
                if (pos == -1) {
                    pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifTiffBEHeader, 4);
                    if (pos != -1) {
                        be_header = true;
                    }
                }

                if (s_header) {
                    offset += 6;
                }
                if (be_header || le_header) {
                    offset += 12;
                }

                const long size = payload.size_ + offset;
                rawExifData = (byte*)malloc(size);

                if (s_header) {
                    us2Data(size_buff, (uint16_t) (size - 6), bigEndian);
                    memcpy(rawExifData, (char*)&exifLongHeader, 4);
                    memcpy(rawExifData + 4, (char*)&size_buff, 2);
                }

                if (be_header || le_header) {
                    us2Data(size_buff, (uint16_t) (size - 6), bigEndian);
                    memcpy(rawExifData, (char*)&exifLongHeader, 4);
                    memcpy(rawExifData + 4, (char*)&size_buff, 2);
                    memcpy(rawExifData + 6, (char*)&exifShortHeader, 6);
                }

                memcpy(rawExifData + offset, payload.pData_, payload.size_);

#ifdef DEBUG
                std::cout << "Display Hex Dump [size:" << (unsigned long)size << "]" << std::endl;
                std::cout << Internal::binaryToHex(rawExifData, size);
#endif

                if (pos != -1) {
                    XmpData  xmpData;
                    ByteOrder bo = ExifParser::decode(exifData_,
                                                      payload.pData_ + pos,
                                                      payload.size_ - pos);
                    setByteOrder(bo);
                }
                else
                {
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << "Failed to decode Exif metadata." << std::endl;
#endif
                    exifData_.clear();
                }

                if (rawExifData) free(rawExifData);
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_XMP)) {
                io_->read(payload.pData_, payload.size_);
                xmpPacket_.assign(reinterpret_cast<char*>(payload.pData_), payload.size_);
                if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) {
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << "Failed to decode XMP metadata." << std::endl;
#endif
                } else {
#ifdef DEBUG
                    std::cout << "Display Hex Dump [size:" << (unsigned long)payload.size_ << "]" << std::endl;
                    std::cout << Internal::binaryToHex(payload.pData_, payload.size_);
#endif
                }
            } else {
                io_->seek(size, BasicIo::cur);
            }

            if ( io_->tell() % 2 ) io_->seek(+1, BasicIo::cur);
        }
    }

A WebPImage::decodeChunks integer overflow in Exiv2 through 0.27.1 allows an attacker to cause a denial of service (large heap allocation followed by a very long running loop) via a crafted WEBP image file.


    void WebPImage::decodeChunks(uint32_t filesize)
    {
        DataBuf   chunkId(5);
        byte      size_buff[WEBP_TAG_SIZE];
        bool      has_canvas_data = false;

#ifdef DEBUG
        std::cout << "Reading metadata" << std::endl;
#endif

        chunkId.pData_[4] = '\0' ;
        while ( !io_->eof() && (uint64_t) io_->tell() < filesize) {
            readOrThrow(*io_, chunkId.pData_, WEBP_TAG_SIZE, Exiv2::kerCorruptedMetadata);
            readOrThrow(*io_, size_buff, WEBP_TAG_SIZE, Exiv2::kerCorruptedMetadata);
            const uint32_t size = Exiv2::getULong(size_buff, littleEndian);
            enforce(io_->tell() <= filesize, Exiv2::kerCorruptedMetadata);
            enforce(size <= (filesize - io_->tell()), Exiv2::kerCorruptedMetadata);

            DataBuf payload(size);

            if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8X) && !has_canvas_data) {
                enforce(size >= 10, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                byte size_buf[WEBP_TAG_SIZE];

                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);

                // Fetch width
                memcpy(&size_buf, &payload.pData_[4], 3);
                size_buf[3] = 0;
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1;

                // Fetch height
                memcpy(&size_buf, &payload.pData_[7], 3);
                size_buf[3] = 0;
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8) && !has_canvas_data) {
                enforce(size >= 10, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);
                byte size_buf[WEBP_TAG_SIZE];

                // Fetch width""
                memcpy(&size_buf, &payload.pData_[6], 2);
                size_buf[2] = 0;
                size_buf[3] = 0;
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff;

                // Fetch height
                memcpy(&size_buf, &payload.pData_[8], 2);
                size_buf[2] = 0;
                size_buf[3] = 0;
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8L) && !has_canvas_data) {
                enforce(size >= 5, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                byte size_buf_w[2];
                byte size_buf_h[3];

                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);

                // Fetch width
                memcpy(&size_buf_w, &payload.pData_[1], 2);
                size_buf_w[1] &= 0x3F;
                pixelWidth_ = Exiv2::getUShort(size_buf_w, littleEndian) + 1;

                // Fetch height
                memcpy(&size_buf_h, &payload.pData_[2], 3);
                size_buf_h[0] = ((size_buf_h[0] >> 6) & 0x3) | ((size_buf_h[1]  & 0x3F) << 0x2);
                size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0xF) << 0x2);
                pixelHeight_ = Exiv2::getUShort(size_buf_h, littleEndian) + 1;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_ANMF) && !has_canvas_data) {
                enforce(size >= 12, Exiv2::kerCorruptedMetadata);

                has_canvas_data = true;
                byte size_buf[WEBP_TAG_SIZE];

                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);

                // Fetch width
                memcpy(&size_buf, &payload.pData_[6], 3);
                size_buf[3] = 0;
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1;

                // Fetch height
                memcpy(&size_buf, &payload.pData_[9], 3);
                size_buf[3] = 0;
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1;
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_ICCP)) {
                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);
                this->setIccProfile(payload);
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_EXIF)) {
                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);

                byte  size_buff[2];
                // 4 meaningful bytes + 2 padding bytes
                byte  exifLongHeader[]   = { 0xFF, 0x01, 0xFF, 0xE1, 0x00, 0x00 };
                byte  exifShortHeader[]  = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
                byte  exifTiffLEHeader[] = { 0x49, 0x49, 0x2A };       // "MM*"
                byte  exifTiffBEHeader[] = { 0x4D, 0x4D, 0x00, 0x2A }; // "II\0*"
                byte* rawExifData = NULL;
                long  offset = 0;
                bool  s_header = false;
                bool  le_header = false;
                bool  be_header = false;
                long  pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifLongHeader, 4);

                if (pos == -1) {
                    pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifLongHeader, 6);
                    if (pos != -1) {
                        s_header = true;
                    }
                }
                if (pos == -1) {
                    pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifTiffLEHeader, 3);
                    if (pos != -1) {
                        le_header = true;
                    }
                }
                if (pos == -1) {
                    pos = getHeaderOffset (payload.pData_, payload.size_, (byte*)&exifTiffBEHeader, 4);
                    if (pos != -1) {
                        be_header = true;
                    }
                }

                if (s_header) {
                    offset += 6;
                }
                if (be_header || le_header) {
                    offset += 12;
                }

                const long size = payload.size_ + offset;
                rawExifData = (byte*)malloc(size);

                if (s_header) {
                    us2Data(size_buff, (uint16_t) (size - 6), bigEndian);
                    memcpy(rawExifData, (char*)&exifLongHeader, 4);
                    memcpy(rawExifData + 4, (char*)&size_buff, 2);
                }

                if (be_header || le_header) {
                    us2Data(size_buff, (uint16_t) (size - 6), bigEndian);
                    memcpy(rawExifData, (char*)&exifLongHeader, 4);
                    memcpy(rawExifData + 4, (char*)&size_buff, 2);
                    memcpy(rawExifData + 6, (char*)&exifShortHeader, 6);
                }

                memcpy(rawExifData + offset, payload.pData_, payload.size_);

#ifdef DEBUG
                std::cout << "Display Hex Dump [size:" << (unsigned long)size << "]" << std::endl;
                std::cout << Internal::binaryToHex(rawExifData, size);
#endif

                if (pos != -1) {
                    XmpData  xmpData;
                    ByteOrder bo = ExifParser::decode(exifData_,
                                                      payload.pData_ + pos,
                                                      payload.size_ - pos);
                    setByteOrder(bo);
                }
                else
                {
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << "Failed to decode Exif metadata." << std::endl;
#endif
                    exifData_.clear();
                }

                if (rawExifData) free(rawExifData);
            } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_XMP)) {
                readOrThrow(*io_, payload.pData_, payload.size_, Exiv2::kerCorruptedMetadata);
                xmpPacket_.assign(reinterpret_cast<char*>(payload.pData_), payload.size_);
                if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) {
#ifndef SUPPRESS_WARNINGS
                    EXV_WARNING << "Failed to decode XMP metadata." << std::endl;
#endif
                } else {
#ifdef DEBUG
                    std::cout << "Display Hex Dump [size:" << (unsigned long)payload.size_ << "]" << std::endl;
                    std::cout << Internal::binaryToHex(payload.pData_, payload.size_);
#endif
                }
            } else {
                io_->seek(size, BasicIo::cur);
            }

            if ( io_->tell() % 2 ) io_->seek(+1, BasicIo::cur);
        }
    }


TRQ-2885 - clean up unit test so it passes when run as root

START_TEST(test_tm_adopt_ispidowner)
  {
  /* we are the owner of this pid so should return true */
  fail_unless(TRUE == ispidowner(getpid()));

  /* assuming the unit test is not run as root, owner */
  /* owner of this pid is not the owner of pid 1 (init) */
  fail_unless(FALSE == ispidowner(1));
  }

The tm_adopt function in lib/Libifl/tm.c in Terascale Open-Source Resource and Queue Manager (aka TORQUE Resource Manager) 5.0.x, 4.5.x, 4.2.x, and earlier does not validate that the owner of the process also owns the adopted session id, which allows remote authenticated users to kill arbitrary processes via a crafted executable.


START_TEST(test_tm_adopt_ispidowner)
  {
  /* we are the owner of this pid so should return true */
  fail_unless(TRUE == ispidowner(getpid()));

  /* when unit test run as non-root user, owner of this pid is not the owner of pid 1 (init) */
  if (getuid() != 0)
    fail_unless(FALSE == ispidowner(1));
  }


CVE-2018-1140 ldb: Check for ldb_dn_get_casefold() failure in ldb_sqlite

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13374

static char *parsetree_to_sql(struct ldb_module *module,
			      void *mem_ctx,
			      const struct ldb_parse_tree *t)
{
	struct ldb_context *ldb;
	const struct ldb_schema_attribute *a;
	struct ldb_val value, subval;
	char *wild_card_string;
	char *child, *tmp;
	char *ret = NULL;
	char *attr;
	unsigned int i;

	ldb = ldb_module_get_ctx(module);

	switch(t->operation) {
	case LDB_OP_AND:

		tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
		if (tmp == NULL) return NULL;

		for (i = 1; i < t->u.list.num_elements; i++) {

			child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
			if (child == NULL) return NULL;

			tmp = talloc_asprintf_append(tmp, " INTERSECT %s ", child);
			if (tmp == NULL) return NULL;
		}

		ret = talloc_asprintf(mem_ctx, "SELECT * FROM ( %s )\n", tmp);

		return ret;

	case LDB_OP_OR:

		tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
		if (tmp == NULL) return NULL;

		for (i = 1; i < t->u.list.num_elements; i++) {

			child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
			if (child == NULL) return NULL;

			tmp = talloc_asprintf_append(tmp, " UNION %s ", child);
			if (tmp == NULL) return NULL;
		}

		return talloc_asprintf(mem_ctx, "SELECT * FROM ( %s ) ", tmp);

	case LDB_OP_NOT:

		child = parsetree_to_sql(module, mem_ctx, t->u.isnot.child);
		if (child == NULL) return NULL;

		return talloc_asprintf(mem_ctx,
					"SELECT eid FROM ldb_entry "
					"WHERE eid NOT IN ( %s ) ", child);

	case LDB_OP_EQUALITY:
		/*
		 * For simple searches, we want to retrieve the list of EIDs that
		 * match the criteria.
		*/
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		if (strcasecmp(t->u.equality.attr, "dn") == 0) {
			/* DN query is a special ldb case */
		 	const char *cdn = ldb_dn_get_casefold(
						ldb_dn_new(mem_ctx, ldb,
							      (const char *)value.data));

			return lsqlite3_tprintf(mem_ctx,
						"SELECT eid FROM ldb_entry "
						"WHERE norm_dn = '%q'", cdn);

		} else {
			/* A normal query. */
			return lsqlite3_tprintf(mem_ctx,
						"SELECT eid FROM ldb_attribute_values "
						"WHERE norm_attr_name = '%q' "
						"AND norm_attr_value = '%q'",
						attr,
						value.data);

		}

	case LDB_OP_SUBSTRING:

		wild_card_string = talloc_strdup(mem_ctx,
					(t->u.substring.start_with_wildcard)?"*":"");
		if (wild_card_string == NULL) return NULL;

		for (i = 0; t->u.substring.chunks[i]; i++) {
			wild_card_string = talloc_asprintf_append(wild_card_string, "%s*",
							t->u.substring.chunks[i]->data);
			if (wild_card_string == NULL) return NULL;
		}

		if ( ! t->u.substring.end_with_wildcard ) {
			/* remove last wildcard */
			wild_card_string[strlen(wild_card_string) - 1] = '\0';
		}

		attr = ldb_attr_casefold(mem_ctx, t->u.substring.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		subval.data = (void *)wild_card_string;
		subval.length = strlen(wild_card_string) + 1;

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(subval), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND norm_attr_value GLOB '%q'",
					attr,
					value.data);

	case LDB_OP_GREATER:
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND ldap_compare(norm_attr_value, '>=', '%q', '%q') ",
					attr,
					value.data,
					attr);

	case LDB_OP_LESS:
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND ldap_compare(norm_attr_value, '<=', '%q', '%q') ",
					attr,
					value.data,
					attr);

	case LDB_OP_PRESENT:
		if (strcasecmp(t->u.present.attr, "dn") == 0) {
			return talloc_strdup(mem_ctx, "SELECT eid FROM ldb_entry");
		}

		attr = ldb_attr_casefold(mem_ctx, t->u.present.attr);
		if (attr == NULL) return NULL;

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' ",
					attr);

	case LDB_OP_APPROX:
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND ldap_compare(norm_attr_value, '~%', 'q', '%q') ",
					attr,
					value.data,
					attr);

	case LDB_OP_EXTENDED:
#warning  "work out how to handle bitops"
		return NULL;

	default:
		break;
	};

	/* should never occur */
	abort();
	return NULL;
}

A missing input sanitization flaw was found in the implementation of LDP database used for the LDAP server. An attacker could use this flaw to cause a denial of service against a samba server, used as a Active Directory Domain Controller. All versions of Samba from 4.8.0 onwards are vulnerable


static char *parsetree_to_sql(struct ldb_module *module,
			      void *mem_ctx,
			      const struct ldb_parse_tree *t)
{
	struct ldb_context *ldb;
	const struct ldb_schema_attribute *a;
	struct ldb_val value, subval;
	char *wild_card_string;
	char *child, *tmp;
	char *ret = NULL;
	char *attr;
	unsigned int i;

	ldb = ldb_module_get_ctx(module);

	switch(t->operation) {
	case LDB_OP_AND:

		tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
		if (tmp == NULL) return NULL;

		for (i = 1; i < t->u.list.num_elements; i++) {

			child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
			if (child == NULL) return NULL;

			tmp = talloc_asprintf_append(tmp, " INTERSECT %s ", child);
			if (tmp == NULL) return NULL;
		}

		ret = talloc_asprintf(mem_ctx, "SELECT * FROM ( %s )\n", tmp);

		return ret;

	case LDB_OP_OR:

		tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
		if (tmp == NULL) return NULL;

		for (i = 1; i < t->u.list.num_elements; i++) {

			child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
			if (child == NULL) return NULL;

			tmp = talloc_asprintf_append(tmp, " UNION %s ", child);
			if (tmp == NULL) return NULL;
		}

		return talloc_asprintf(mem_ctx, "SELECT * FROM ( %s ) ", tmp);

	case LDB_OP_NOT:

		child = parsetree_to_sql(module, mem_ctx, t->u.isnot.child);
		if (child == NULL) return NULL;

		return talloc_asprintf(mem_ctx,
					"SELECT eid FROM ldb_entry "
					"WHERE eid NOT IN ( %s ) ", child);

	case LDB_OP_EQUALITY:
		/*
		 * For simple searches, we want to retrieve the list of EIDs that
		 * match the criteria.
		*/
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		if (strcasecmp(t->u.equality.attr, "dn") == 0) {
			/* DN query is a special ldb case */
		 	const char *cdn = ldb_dn_get_casefold(
						ldb_dn_new(mem_ctx, ldb,
							      (const char *)value.data));
			if (cdn == NULL) {
				return NULL;
			}

			return lsqlite3_tprintf(mem_ctx,
						"SELECT eid FROM ldb_entry "
						"WHERE norm_dn = '%q'", cdn);

		} else {
			/* A normal query. */
			return lsqlite3_tprintf(mem_ctx,
						"SELECT eid FROM ldb_attribute_values "
						"WHERE norm_attr_name = '%q' "
						"AND norm_attr_value = '%q'",
						attr,
						value.data);

		}

	case LDB_OP_SUBSTRING:

		wild_card_string = talloc_strdup(mem_ctx,
					(t->u.substring.start_with_wildcard)?"*":"");
		if (wild_card_string == NULL) return NULL;

		for (i = 0; t->u.substring.chunks[i]; i++) {
			wild_card_string = talloc_asprintf_append(wild_card_string, "%s*",
							t->u.substring.chunks[i]->data);
			if (wild_card_string == NULL) return NULL;
		}

		if ( ! t->u.substring.end_with_wildcard ) {
			/* remove last wildcard */
			wild_card_string[strlen(wild_card_string) - 1] = '\0';
		}

		attr = ldb_attr_casefold(mem_ctx, t->u.substring.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		subval.data = (void *)wild_card_string;
		subval.length = strlen(wild_card_string) + 1;

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(subval), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND norm_attr_value GLOB '%q'",
					attr,
					value.data);

	case LDB_OP_GREATER:
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND ldap_compare(norm_attr_value, '>=', '%q', '%q') ",
					attr,
					value.data,
					attr);

	case LDB_OP_LESS:
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND ldap_compare(norm_attr_value, '<=', '%q', '%q') ",
					attr,
					value.data,
					attr);

	case LDB_OP_PRESENT:
		if (strcasecmp(t->u.present.attr, "dn") == 0) {
			return talloc_strdup(mem_ctx, "SELECT eid FROM ldb_entry");
		}

		attr = ldb_attr_casefold(mem_ctx, t->u.present.attr);
		if (attr == NULL) return NULL;

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' ",
					attr);

	case LDB_OP_APPROX:
		attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
		if (attr == NULL) return NULL;
		a = ldb_schema_attribute_by_name(ldb, attr);

		/* Get a canonicalised copy of the data */
		a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
		if (value.data == NULL) {
			return NULL;
		}

		return lsqlite3_tprintf(mem_ctx,
					"SELECT eid FROM ldb_attribute_values "
					"WHERE norm_attr_name = '%q' "
					"AND ldap_compare(norm_attr_value, '~%', 'q', '%q') ",
					attr,
					value.data,
					attr);

	case LDB_OP_EXTENDED:
#warning  "work out how to handle bitops"
		return NULL;

	default:
		break;
	};

	/* should never occur */
	abort();
	return NULL;
}


block/cloop: validate block_size header field (CVE-2014-0144)

Avoid unbounded s->uncompressed_block memory allocation by checking that
the block_size header field has a reasonable value.  Also enforce the
assumption that the value is a non-zero multiple of 512.

These constraints conform to cloop 2.639's code so we accept existing
image files.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
                      Error **errp)
{
    BDRVCloopState *s = bs->opaque;
    uint32_t offsets_size, max_compressed_block_size = 1, i;
    int ret;

    bs->read_only = 1;

    /* read header */
    ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
    if (ret < 0) {
        return ret;
    }
    s->block_size = be32_to_cpu(s->block_size);

    ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
    if (ret < 0) {
        return ret;
    }
    s->n_blocks = be32_to_cpu(s->n_blocks);

    /* read offsets */
    offsets_size = s->n_blocks * sizeof(uint64_t);
    s->offsets = g_malloc(offsets_size);

    ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
    if (ret < 0) {
        goto fail;
    }

    for(i=0;i<s->n_blocks;i++) {
        s->offsets[i] = be64_to_cpu(s->offsets[i]);
        if (i > 0) {
            uint32_t size = s->offsets[i] - s->offsets[i - 1];
            if (size > max_compressed_block_size) {
                max_compressed_block_size = size;
            }
        }
    }

    /* initialize zlib engine */
    s->compressed_block = g_malloc(max_compressed_block_size + 1);
    s->uncompressed_block = g_malloc(s->block_size);
    if (inflateInit(&s->zstream) != Z_OK) {
        ret = -EINVAL;
        goto fail;
    }
    s->current_block = s->n_blocks;

    s->sectors_per_block = s->block_size/512;
    bs->total_sectors = s->n_blocks * s->sectors_per_block;
    qemu_co_mutex_init(&s->lock);
    return 0;

fail:
    g_free(s->offsets);
    g_free(s->compressed_block);
    g_free(s->uncompressed_block);
    return ret;
}

QEMU before 2.0.0 block drivers for CLOOP, QCOW2 version 2 and various other image formats are vulnerable to potential memory corruptions, integer/buffer overflows or crash caused by missing input validations which could allow a remote user to execute arbitrary code on the host with the privileges of the QEMU process.


static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
                      Error **errp)
{
    BDRVCloopState *s = bs->opaque;
    uint32_t offsets_size, max_compressed_block_size = 1, i;
    int ret;

    bs->read_only = 1;

    /* read header */
    ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
    if (ret < 0) {
        return ret;
    }
    s->block_size = be32_to_cpu(s->block_size);
    if (s->block_size % 512) {
        error_setg(errp, "block_size %u must be a multiple of 512",
                   s->block_size);
        return -EINVAL;
    }
    if (s->block_size == 0) {
        error_setg(errp, "block_size cannot be zero");
        return -EINVAL;
    }

    /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but
     * we can accept more.  Prevent ridiculous values like 4 GB - 1 since we
     * need a buffer this big.
     */
    if (s->block_size > MAX_BLOCK_SIZE) {
        error_setg(errp, "block_size %u must be %u MB or less",
                   s->block_size,
                   MAX_BLOCK_SIZE / (1024 * 1024));
        return -EINVAL;
    }

    ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
    if (ret < 0) {
        return ret;
    }
    s->n_blocks = be32_to_cpu(s->n_blocks);

    /* read offsets */
    offsets_size = s->n_blocks * sizeof(uint64_t);
    s->offsets = g_malloc(offsets_size);

    ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
    if (ret < 0) {
        goto fail;
    }

    for(i=0;i<s->n_blocks;i++) {
        s->offsets[i] = be64_to_cpu(s->offsets[i]);
        if (i > 0) {
            uint32_t size = s->offsets[i] - s->offsets[i - 1];
            if (size > max_compressed_block_size) {
                max_compressed_block_size = size;
            }
        }
    }

    /* initialize zlib engine */
    s->compressed_block = g_malloc(max_compressed_block_size + 1);
    s->uncompressed_block = g_malloc(s->block_size);
    if (inflateInit(&s->zstream) != Z_OK) {
        ret = -EINVAL;
        goto fail;
    }
    s->current_block = s->n_blocks;

    s->sectors_per_block = s->block_size/512;
    bs->total_sectors = s->n_blocks * s->sectors_per_block;
    qemu_co_mutex_init(&s->lock);
    return 0;

fail:
    g_free(s->offsets);
    g_free(s->compressed_block);
    g_free(s->uncompressed_block);
    return ret;
}


fixed invalid read

fixes https://oss-fuzz.com/testcase-detail/5765246676631552

static int sc_pkcs15emu_gemsafeGPK_init(sc_pkcs15_card_t *p15card)
{
	const cdata certs[] = {
		{"User certificate",0, "","1", 0},
		{NULL, 0, NULL, NULL, 0}
	};

	const pindata pins[] = {
		{ "01", "pin", "3F000200", 0x00,
		  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
		  8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
		  SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00,
		  SC_PKCS15_CO_FLAG_PRIVATE },
		{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	};

	const prdata prkeys[] = {
		{ "01", "AUTH key", 1024, USAGE_AUT, "I0009",
		  0x00, "01", 0},
		{ NULL, NULL, 0, 0, NULL, 0, NULL, 0}
	};

	int    r, i, j;
	int dfpath;
	sc_path_t path;
	sc_file_t *file = NULL;
	sc_card_t *card = p15card->card;
	unsigned char *gsdata = NULL;
	unsigned int idxlen, idx1, idx2, seq_len1, seq_len2, seq_size1, seq_size2;
	sc_serial_number_t serial;

	u8 sysrec[7];
	int num_keyinfo = 0;
	keyinfo kinfo[8]; /* will look for 8 keys */
	u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */
	u8 *cp;
	char buf[256];

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	/* need to limit to 248 */
	card->max_send_size = 248;
	card->max_recv_size = 248;

	/* could read this off card if needed */

	p15card->tokeninfo->label = strdup("GemSAFE");
	p15card->tokeninfo->manufacturer_id = strdup(MANU_ID);
	/* get serial number */
	r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
	if (r != SC_SUCCESS)
		return SC_ERROR_INTERNAL;
	r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
	if (r != SC_SUCCESS)
		return SC_ERROR_INTERNAL;
	p15card->tokeninfo->serial_number = strdup(buf);

	/* test if we have a gemsafe app df */
	memset(&path, 0, sizeof(path));
	memcpy(path.value, gemsafe_aid, sizeof(gemsafe_aid));
	path.len = sizeof(gemsafe_aid);
	path.type = SC_PATH_TYPE_DF_NAME;
	r = sc_select_file(card, &path, &file);
	if (r < 0) {
		/* OK, then lets try for 3f000200 */
		sc_format_path("3F000200",&path);
		path.type = SC_PATH_TYPE_PATH;
		r = sc_select_file(card, &path, &file);
	}

	if (r < 0)
		return SC_ERROR_WRONG_CARD;

	/* we will use dfpath in all other references */
	dfpath = file->id;
	sc_file_free(file);
	file = NULL;

	sc_log(card->ctx,  "GemSafe file found, id=%d",dfpath);

	/* There may be more then one key in the directory. */
	/* we need to find them so we can associate them with the */
	/* the certificate.  The files are 0007 to 000f */

	for (i = 7; i < 16; i++) {
		path.value[0] = 0x00;
		path.value[1] = i;
		path.len = 2;
		path.type = SC_PATH_TYPE_FILE_ID;
		r = sc_select_file(card, &path, NULL);
		if (r < 0)
			continue;
		r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR);
		if (r != 7 || sysrec[0] != 0) {
			continue;
		}
		if (sysrec[5] != 0x00) {
			continue;
		}

		switch (sysrec[1]) {
			case 0x00: kinfo[num_keyinfo].modulus_len =  512 / 8; break;
			case 0x10: kinfo[num_keyinfo].modulus_len =  768 / 8; break;
			case 0x11: kinfo[num_keyinfo].modulus_len = 1024 / 8; break;
			default:
				sc_log(card->ctx,  "Unsupported modulus length");
				continue;
		}

		kinfo[num_keyinfo].fileid = i;
		sc_pkcs15_format_id("", &kinfo[num_keyinfo].id);

		sc_log(card->ctx, "reading modulus");
		r = sc_read_record(card, 2, modulus_buf,
				kinfo[num_keyinfo].modulus_len+1, SC_RECORD_BY_REC_NR);
		if (r < 0)
			continue;

		/* need to reverse the modulus skipping the tag */
		j = kinfo[num_keyinfo].modulus_len;
		cp = kinfo[num_keyinfo].modulus;
		while (j--)
			*cp++ =  modulus_buf[j + 1];
		num_keyinfo++;
	}

	/* Get the gemsafe data with the cert */
	 sc_format_path("3F000200004", &path);

	/* file.id has the real DF of the GemSAFE file from above*/
	 path.value[2] = dfpath >> 8;
	 path.value[3] = dfpath & 0xff;

	if (sc_select_file(card, &path, &file) < 0) {
		return SC_ERROR_WRONG_CARD;
	}

	/* the GemSAFE file has our cert, but we do not know the format */
	/* of the file. But we do know a cert has SEQ SEQ SEQOF INT 2   */
	/* so we will look for that. We assume cert is larger then 127 bytes */
	/* and less then 65K, and must be fit in the file->size */
	/* There is a chance that we might find something that is not */
	/* a cert, but the chances are low. If GemPlus ever publishes */
	/* the format of the file, we can used that instead. */

	/* For performance reasons we will only */
	/* read part of the file , as it is about 6100 bytes */

	gsdata = malloc(file->size);

	if (!gsdata)
		return SC_ERROR_OUT_OF_MEMORY;

	/* set indices of data in gsdata  */
	idx1 = 0; /* start point */
	idx2 = 0; /* index of last data read so far */

	/* set certs  We only have one we are interested in */
	/* but the read loop is set up to allow for more in future */

	for (i = 0; certs[i].label; i++) {
		struct sc_pkcs15_cert_info cert_info;
		struct sc_pkcs15_object    cert_obj;
		sc_pkcs15_cert_t 		*cert_out;

		memset(&cert_info, 0, sizeof(cert_info));
		memset(&cert_obj,  0, sizeof(cert_obj));

		sc_pkcs15_format_id(certs[i].id, &cert_info.id);
		cert_info.authority = certs[i].authority;

		strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label));
		cert_obj.flags = certs[i].obj_flags;

		while (idx1 < file->size - 16) { /* actually 13 for all these tests */
			if (idx1 + 16 > idx2 ) { 	/* need more data in buff */
				idxlen = 248; 		/* read in next 248 bytes */
				if (idxlen > file->size - idx2)
					idxlen = file->size - idx2;
				r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0);
				if (r < 0)
					break;
				idx2 = idx2 + idxlen;
			}

			if ( gsdata[idx1] == 0x30 &&
					is_seq(gsdata + idx1, &seq_size1, &seq_len1) &&
					is_seq(gsdata + idx1 + seq_size1, &seq_size2, &seq_len2) &&
					gsdata[idx1 + seq_size1 + seq_size2 + 0] == 0xa0 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 1] == 0x03 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 2] == 0x02 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 3] == 0x01 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 4] == 0x02 &&
					idx1 + 4 + seq_len1 < file->size) {
				/* we have a cert (I hope) */
				/* read in rest if needed */
				idxlen = idx1 + seq_len1 + 4 - idx2;
				if (idxlen > 0) {
					idxlen = (idxlen + 3) & 0xfffffffc;
					r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0);
					if (r < 0)
						break; /* can not read cert */
					idx2 = idx2 + idxlen;
				}
				cert_info.value.len = seq_len1 + 4;
				sc_log(card->ctx,  "Found cert at offset %d", idx1);
				cert_info.value.value = (unsigned char *)
						malloc(cert_info.value.len);
				if (!cert_info.value.value)
					return SC_ERROR_OUT_OF_MEMORY;

				memcpy(cert_info.value.value, gsdata + idx1, cert_info.value.len);
				idx1 = idx1 + cert_info.value.len;
				break;
			}
			idx1++;
		}

		if (cert_info.value.value == NULL)
			break; /* cert not found, no more certs */

		r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
		if (r < 0) {
			free(gsdata);
			return SC_ERROR_INTERNAL;
		}

		/* now lets see if we have a matching key for this cert */

		r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
		if (r < 0) {
			free(gsdata);
			return SC_ERROR_INTERNAL;
		}

		for (j = 0; j < num_keyinfo; j++) {
			if (cert_out->key->u.rsa.modulus.len == kinfo[j].modulus_len &&
					memcmp(cert_out->key->u.rsa.modulus.data,
					&kinfo[j].modulus, cert_out->key->u.rsa.modulus.len) == 0) {
			memcpy(&kinfo[j].id, &cert_info.id, sizeof(sc_pkcs15_id_t));
			sc_log(card->ctx,  "found match");
			}
		}
		sc_pkcs15_free_certificate(cert_out);
	}

	if (gsdata)
		free(gsdata);

	/* set pins */

	/* GemSAFE uses different padding, so need to trap */
	/* the pin_cmd and reset the padding */

	pin_cmd_save = card->ops->pin_cmd;
	card->ops->pin_cmd = my_pin_cmd;

	for (i = 0; pins[i].label; i++) {
		struct sc_pkcs15_auth_info pin_info;
		struct sc_pkcs15_object   pin_obj;

		memset(&pin_info, 0, sizeof(pin_info));
		memset(&pin_obj,  0, sizeof(pin_obj));

		sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
		pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
		pin_info.attrs.pin.reference     = pins[i].ref;
		pin_info.attrs.pin.flags         = pins[i].flags;
		pin_info.attrs.pin.type          = pins[i].type;
		pin_info.attrs.pin.min_length    = pins[i].minlen;
		pin_info.attrs.pin.stored_length = pins[i].storedlen;
		pin_info.attrs.pin.max_length    = pins[i].maxlen;
		pin_info.attrs.pin.pad_char      = pins[i].pad_char;
		sc_format_path(pins[i].path, &pin_info.path);
		pin_info.path.value[2] = dfpath >> 8;
		pin_info.path.value[3] = dfpath & 0xff;
		pin_info.tries_left    = -1;
		pin_info.logged_in = SC_PIN_STATE_UNKNOWN;

		strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label));
		pin_obj.flags = pins[i].obj_flags;

		r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
		if (r < 0)
			return SC_ERROR_INTERNAL;
	}

	/* needs work, as we may want to add more then one key */
	/* but not sure what the other keys do */

	/* set private keys */
	for (i = 0; prkeys[i].label; i++) {
		struct sc_pkcs15_prkey_info prkey_info;
		struct sc_pkcs15_object     prkey_obj;

		memset(&prkey_info, 0, sizeof(prkey_info));
		memset(&prkey_obj,  0, sizeof(prkey_obj));

		sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id);
		prkey_info.usage         = prkeys[i].usage;
		prkey_info.native        = 1;
		prkey_info.key_reference = prkeys[i].ref;
		prkey_info.modulus_length= prkeys[i].modulus_len;
		sc_format_path(prkeys[i].path, &prkey_info.path);

		/*DEE need to look for them by reading and checking modulus vs cert */

 		/* will use the default path, unless we found a key with */
		/* the same modulus as the cert(s) we already added */
		/* This allows us to have a card with a key but no cert */

		for (j = 0; j < num_keyinfo; j++) {
			if (sc_pkcs15_compare_id(&kinfo[j].id, &prkey_info.id))  {
				sc_log(card->ctx,  "found key in file %d for id %s",
					 kinfo[j].fileid, prkeys[i].id);
				prkey_info.path.value[0] = kinfo[j].fileid >> 8;
				prkey_info.path.value[1] = kinfo[j].fileid & 0xff;
				break;
			}
		}

		strlcpy(prkey_obj.label, prkeys[i].label, sizeof(prkey_obj.label));
		prkey_obj.flags = prkeys[i].obj_flags;
		if (prkeys[i].auth_id)
			sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id);

		r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
		if (r < 0)
			return SC_ERROR_INTERNAL;
	}
	return SC_SUCCESS;
}

The gemsafe GPK smart card software driver in OpenSC before 0.21.0-rc1 has a stack-based buffer overflow in sc_pkcs15emu_gemsafeGPK_init.


static int sc_pkcs15emu_gemsafeGPK_init(sc_pkcs15_card_t *p15card)
{
	const cdata certs[] = {
		{"User certificate",0, "","1", 0},
		{NULL, 0, NULL, NULL, 0}
	};

	const pindata pins[] = {
		{ "01", "pin", "3F000200", 0x00,
		  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
		  8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
		  SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00,
		  SC_PKCS15_CO_FLAG_PRIVATE },
		{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	};

	const prdata prkeys[] = {
		{ "01", "AUTH key", 1024, USAGE_AUT, "I0009",
		  0x00, "01", 0},
		{ NULL, NULL, 0, 0, NULL, 0, NULL, 0}
	};

	int    r, i, j;
	int dfpath;
	sc_path_t path;
	sc_file_t *file = NULL;
	sc_card_t *card = p15card->card;
	unsigned char *gsdata = NULL;
	unsigned int idxlen, idx1, idx2, seq_len1, seq_len2, seq_size1, seq_size2;
	sc_serial_number_t serial;

	u8 sysrec[7];
	int num_keyinfo = 0;
	keyinfo kinfo[9]; /* will look for 9 keys */
	u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */
	u8 *cp;
	char buf[256];

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	/* need to limit to 248 */
	card->max_send_size = 248;
	card->max_recv_size = 248;

	/* could read this off card if needed */

	p15card->tokeninfo->label = strdup("GemSAFE");
	p15card->tokeninfo->manufacturer_id = strdup(MANU_ID);
	/* get serial number */
	r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
	if (r != SC_SUCCESS)
		return SC_ERROR_INTERNAL;
	r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
	if (r != SC_SUCCESS)
		return SC_ERROR_INTERNAL;
	p15card->tokeninfo->serial_number = strdup(buf);

	/* test if we have a gemsafe app df */
	memset(&path, 0, sizeof(path));
	memcpy(path.value, gemsafe_aid, sizeof(gemsafe_aid));
	path.len = sizeof(gemsafe_aid);
	path.type = SC_PATH_TYPE_DF_NAME;
	r = sc_select_file(card, &path, &file);
	if (r < 0) {
		/* OK, then lets try for 3f000200 */
		sc_format_path("3F000200",&path);
		path.type = SC_PATH_TYPE_PATH;
		r = sc_select_file(card, &path, &file);
	}

	if (r < 0)
		return SC_ERROR_WRONG_CARD;

	/* we will use dfpath in all other references */
	dfpath = file->id;
	sc_file_free(file);
	file = NULL;

	sc_log(card->ctx,  "GemSafe file found, id=%d",dfpath);

	/* There may be more then one key in the directory. */
	/* we need to find them so we can associate them with the */
	/* the certificate.  The files are 0007 to 000F */

	for (i = 0x7; i <= 0xF; i++) {
		path.value[0] = 0x00;
		path.value[1] = i;
		path.len = 2;
		path.type = SC_PATH_TYPE_FILE_ID;
		r = sc_select_file(card, &path, NULL);
		if (r < 0)
			continue;
		r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR);
		if (r != 7 || sysrec[0] != 0) {
			continue;
		}
		if (sysrec[5] != 0x00) {
			continue;
		}

		switch (sysrec[1]) {
			case 0x00: kinfo[num_keyinfo].modulus_len =  512 / 8; break;
			case 0x10: kinfo[num_keyinfo].modulus_len =  768 / 8; break;
			case 0x11: kinfo[num_keyinfo].modulus_len = 1024 / 8; break;
			default:
				sc_log(card->ctx,  "Unsupported modulus length");
				continue;
		}

		kinfo[num_keyinfo].fileid = i;
		sc_pkcs15_format_id("", &kinfo[num_keyinfo].id);

		sc_log(card->ctx, "reading modulus");
		r = sc_read_record(card, 2, modulus_buf,
				kinfo[num_keyinfo].modulus_len+1, SC_RECORD_BY_REC_NR);
		if (r < 0)
			continue;

		/* need to reverse the modulus skipping the tag */
		j = kinfo[num_keyinfo].modulus_len;
		cp = kinfo[num_keyinfo].modulus;
		while (j--)
			*cp++ =  modulus_buf[j + 1];
		num_keyinfo++;
	}

	/* Get the gemsafe data with the cert */
	 sc_format_path("3F000200004", &path);

	/* file.id has the real DF of the GemSAFE file from above*/
	 path.value[2] = dfpath >> 8;
	 path.value[3] = dfpath & 0xff;

	if (sc_select_file(card, &path, &file) < 0) {
		return SC_ERROR_WRONG_CARD;
	}

	/* the GemSAFE file has our cert, but we do not know the format */
	/* of the file. But we do know a cert has SEQ SEQ SEQOF INT 2   */
	/* so we will look for that. We assume cert is larger then 127 bytes */
	/* and less then 65K, and must be fit in the file->size */
	/* There is a chance that we might find something that is not */
	/* a cert, but the chances are low. If GemPlus ever publishes */
	/* the format of the file, we can used that instead. */

	/* For performance reasons we will only */
	/* read part of the file , as it is about 6100 bytes */

	gsdata = malloc(file->size);

	if (!gsdata)
		return SC_ERROR_OUT_OF_MEMORY;

	/* set indices of data in gsdata  */
	idx1 = 0; /* start point */
	idx2 = 0; /* index of last data read so far */

	/* set certs  We only have one we are interested in */
	/* but the read loop is set up to allow for more in future */

	for (i = 0; certs[i].label; i++) {
		struct sc_pkcs15_cert_info cert_info;
		struct sc_pkcs15_object    cert_obj;
		sc_pkcs15_cert_t 		*cert_out;

		memset(&cert_info, 0, sizeof(cert_info));
		memset(&cert_obj,  0, sizeof(cert_obj));

		sc_pkcs15_format_id(certs[i].id, &cert_info.id);
		cert_info.authority = certs[i].authority;

		strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label));
		cert_obj.flags = certs[i].obj_flags;

		while (idx1 < file->size - 16) { /* actually 13 for all these tests */
			if (idx1 + 16 > idx2 ) { 	/* need more data in buff */
				idxlen = 248; 		/* read in next 248 bytes */
				if (idxlen > file->size - idx2)
					idxlen = file->size - idx2;
				r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0);
				if (r < 0)
					break;
				idx2 = idx2 + idxlen;
			}

			if ( gsdata[idx1] == 0x30 &&
					is_seq(gsdata + idx1, &seq_size1, &seq_len1) &&
					is_seq(gsdata + idx1 + seq_size1, &seq_size2, &seq_len2) &&
					gsdata[idx1 + seq_size1 + seq_size2 + 0] == 0xa0 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 1] == 0x03 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 2] == 0x02 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 3] == 0x01 &&
					gsdata[idx1 + seq_size1 + seq_size2 + 4] == 0x02 &&
					idx1 + 4 + seq_len1 < file->size) {
				/* we have a cert (I hope) */
				/* read in rest if needed */
				idxlen = idx1 + seq_len1 + 4 - idx2;
				if (idxlen > 0) {
					idxlen = (idxlen + 3) & 0xfffffffc;
					r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0);
					if (r < 0)
						break; /* can not read cert */
					idx2 = idx2 + idxlen;
				}
				cert_info.value.len = seq_len1 + 4;
				sc_log(card->ctx,  "Found cert at offset %d", idx1);
				cert_info.value.value = (unsigned char *)
						malloc(cert_info.value.len);
				if (!cert_info.value.value)
					return SC_ERROR_OUT_OF_MEMORY;

				memcpy(cert_info.value.value, gsdata + idx1, cert_info.value.len);
				idx1 = idx1 + cert_info.value.len;
				break;
			}
			idx1++;
		}

		if (cert_info.value.value == NULL)
			break; /* cert not found, no more certs */

		r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
		if (r < 0) {
			free(gsdata);
			return SC_ERROR_INTERNAL;
		}

		/* now lets see if we have a matching key for this cert */

		r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
		if (r < 0) {
			free(gsdata);
			return SC_ERROR_INTERNAL;
		}

		for (j = 0; j < num_keyinfo; j++) {
			if (cert_out->key->u.rsa.modulus.len == kinfo[j].modulus_len &&
					memcmp(cert_out->key->u.rsa.modulus.data,
					&kinfo[j].modulus, cert_out->key->u.rsa.modulus.len) == 0) {
			memcpy(&kinfo[j].id, &cert_info.id, sizeof(sc_pkcs15_id_t));
			sc_log(card->ctx,  "found match");
			}
		}
		sc_pkcs15_free_certificate(cert_out);
	}

	if (gsdata)
		free(gsdata);

	/* set pins */

	/* GemSAFE uses different padding, so need to trap */
	/* the pin_cmd and reset the padding */

	pin_cmd_save = card->ops->pin_cmd;
	card->ops->pin_cmd = my_pin_cmd;

	for (i = 0; pins[i].label; i++) {
		struct sc_pkcs15_auth_info pin_info;
		struct sc_pkcs15_object   pin_obj;

		memset(&pin_info, 0, sizeof(pin_info));
		memset(&pin_obj,  0, sizeof(pin_obj));

		sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
		pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
		pin_info.attrs.pin.reference     = pins[i].ref;
		pin_info.attrs.pin.flags         = pins[i].flags;
		pin_info.attrs.pin.type          = pins[i].type;
		pin_info.attrs.pin.min_length    = pins[i].minlen;
		pin_info.attrs.pin.stored_length = pins[i].storedlen;
		pin_info.attrs.pin.max_length    = pins[i].maxlen;
		pin_info.attrs.pin.pad_char      = pins[i].pad_char;
		sc_format_path(pins[i].path, &pin_info.path);
		pin_info.path.value[2] = dfpath >> 8;
		pin_info.path.value[3] = dfpath & 0xff;
		pin_info.tries_left    = -1;
		pin_info.logged_in = SC_PIN_STATE_UNKNOWN;

		strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label));
		pin_obj.flags = pins[i].obj_flags;

		r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
		if (r < 0)
			return SC_ERROR_INTERNAL;
	}

	/* needs work, as we may want to add more then one key */
	/* but not sure what the other keys do */

	/* set private keys */
	for (i = 0; prkeys[i].label; i++) {
		struct sc_pkcs15_prkey_info prkey_info;
		struct sc_pkcs15_object     prkey_obj;

		memset(&prkey_info, 0, sizeof(prkey_info));
		memset(&prkey_obj,  0, sizeof(prkey_obj));

		sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id);
		prkey_info.usage         = prkeys[i].usage;
		prkey_info.native        = 1;
		prkey_info.key_reference = prkeys[i].ref;
		prkey_info.modulus_length= prkeys[i].modulus_len;
		sc_format_path(prkeys[i].path, &prkey_info.path);

		/*DEE need to look for them by reading and checking modulus vs cert */

 		/* will use the default path, unless we found a key with */
		/* the same modulus as the cert(s) we already added */
		/* This allows us to have a card with a key but no cert */

		for (j = 0; j < num_keyinfo; j++) {
			if (sc_pkcs15_compare_id(&kinfo[j].id, &prkey_info.id))  {
				sc_log(card->ctx,  "found key in file %d for id %s",
					 kinfo[j].fileid, prkeys[i].id);
				prkey_info.path.value[0] = kinfo[j].fileid >> 8;
				prkey_info.path.value[1] = kinfo[j].fileid & 0xff;
				break;
			}
		}

		strlcpy(prkey_obj.label, prkeys[i].label, sizeof(prkey_obj.label));
		prkey_obj.flags = prkeys[i].obj_flags;
		if (prkeys[i].auth_id)
			sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id);

		r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
		if (r < 0)
			return SC_ERROR_INTERNAL;
	}
	return SC_SUCCESS;
}


Prevent infinite recursion with nested table and textarea

Bug-Debian: https://github.com/tats/w3m/issues/20

renderCoTable(struct table *tbl, int maxlimit)
{
    struct readbuffer obuf;
    struct html_feed_environ h_env;
    struct environment envs[MAX_ENV_LEVEL];
    struct table *t;
    int i, col, row;
    int indent, maxwidth;

    for (i = 0; i < tbl->ntable; i++) {
	t = tbl->tables[i].ptr;
	col = tbl->tables[i].col;
	row = tbl->tables[i].row;
	indent = tbl->tables[i].indent;

	init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tables[i].buf,
		  get_spec_cell_width(tbl, row, col), indent);
	check_row(tbl, row);
	if (h_env.limit > maxlimit)
	    h_env.limit = maxlimit;
	if (t->total_width == 0)
	    maxwidth = h_env.limit - indent;
	else if (t->total_width > 0)
	    maxwidth = t->total_width;
	else
	    maxwidth = t->total_width = -t->total_width * h_env.limit / 100;
	renderTable(t, maxwidth, &h_env);
    }
}

An issue was discovered in the Tatsuya Kinoshita w3m fork before 0.5.3-31. Infinite recursion vulnerability in w3m allows remote attackers to cause a denial of service via a crafted HTML page.


renderCoTable(struct table *tbl, int maxlimit)
{
    struct readbuffer obuf;
    struct html_feed_environ h_env;
    struct environment envs[MAX_ENV_LEVEL];
    struct table *t;
    int i, col, row;
    int indent, maxwidth;
    static struct table *tbl_prev = NULL;
    static int cotbl_count;
#define MAX_COTABLE 100

    if (tbl == NULL)
	return;
    if (tbl != tbl_prev) {
	tbl_prev = tbl;
	cotbl_count = 0;
    }
    if (cotbl_count >= MAX_COTABLE)
	return;	/* workaround to prevent infinite recursion */
    cotbl_count++;

    for (i = 0; i < tbl->ntable; i++) {
	t = tbl->tables[i].ptr;
	col = tbl->tables[i].col;
	row = tbl->tables[i].row;
	indent = tbl->tables[i].indent;

	init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tables[i].buf,
		  get_spec_cell_width(tbl, row, col), indent);
	check_row(tbl, row);
	if (h_env.limit > maxlimit)
	    h_env.limit = maxlimit;
	if (t->total_width == 0)
	    maxwidth = h_env.limit - indent;
	else if (t->total_width > 0)
	    maxwidth = t->total_width;
	else
	    maxwidth = t->total_width = -t->total_width * h_env.limit / 100;
	renderTable(t, maxwidth, &h_env);
    }
}