CVE-2017-8294 (C, published 2017-04-27T14:59Z)
Description: value: libyara/re.c in the regex component in YARA 3.5.0 allows remote attackers to cause a denial of service (out-of-bounds read and application crash) via a crafted rule that is mishandled in the yr_re_exec function.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/VirusTotal/yara
Files changed:
libyara/exec.c
libyara/include/yara/re.h
libyara/re.c
libyara/scan.c
tests/test-rules.c
Commit message: Fix issue #646 (#648)

* Fix issue #646 and some edge cases with wide regexps using \b and \B

* Rename function IS_WORD_CHAR to _yr_re_is_word_char


Vulnerable code:
#define RE_FLAGS_NOT_AT_START          0x100
#define CHAR_IN_CLASS(chr, cls)  \
    ((cls)[(chr) / 8] & 1 << ((chr) % 8))


#define IS_WORD_CHAR(chr) \
    (isalnum(chr) || (chr) == '_')


    size_t input_size,
    size_t input_size,
// Executes a regular expression
//   size_t input_size                - Input data size
//      RE_FLAGS_NOT_AT_START
    size_t input_size,
  #define prolog if (bytes_matched >= max_bytes_matched) \
      }
  #define fail_if_error(e) switch (e) { \
      }

  max_bytes_matched = (int) yr_min(input_size, RE_SCAN_LIMIT);
  // character_size is 2 and input_size is odd we are ignoring the
          match = IS_WORD_CHAR(*input);
          match = !IS_WORD_CHAR(*input);
          if (bytes_matched == 0 &&
              !(flags & RE_FLAGS_NOT_AT_START) &&
              !(flags & RE_FLAGS_BACKWARDS))
          else if (IS_WORD_CHAR(*(input - input_incr)) != IS_WORD_CHAR(*input))
            match = TRUE;
            match = FALSE;
            kill = input_size > (size_t) bytes_matched;
            kill = (flags & RE_FLAGS_NOT_AT_START) || (bytes_matched != 0);
                 input_size > (size_t) bytes_matched;
    if (flags & RE_FLAGS_WIDE &&
        bytes_matched < max_bytes_matched &&
        *(input + 1) != 0)
    {
      _yr_re_fiber_kill_all(&fibers, &storage->fiber_pool);
    }

    size_t input_size,
  int max_bytes_matched = input_size;
    size_t input_size,
        offset > 0 ? flags | RE_FLAGS_NOT_AT_START : flags,
        offset > 0 ? flags | RE_FLAGS_NOT_AT_START : flags,

Fixed code:
          0,
    size_t input_forwards_size,
    size_t input_backwards_size,
    size_t input_forwards_size,
    size_t input_backwards_size,
#define CHAR_IN_CLASS(chr, cls)  \
    ((cls)[(chr) / 8] & 1 << ((chr) % 8))


int _yr_re_is_word_char(
    uint8_t* input,
    int character_size)
{
  int result = ((isalnum(*input) || (*input) == '_'));

  if (character_size == 2)
    result = result && (*(input + 1) == 0);

  return result;
}



      0,
// Executes a regular expression. The specified regular expression will try to
// match the data starting at the address specified by "input". The "input"
// pointer can point to any address inside a memory buffer. Arguments
// "input_forwards_size" and "input_backwards_size" indicate how many bytes
// can be accesible starting at "input" and going forwards and backwards
// respectively.
//
//   <--- input_backwards_size -->|<----------- input_forwards_size -------->
//  |--------  memory buffer  -----------------------------------------------|
//                                ^
//                              input
//   size_t input_forwards_size       - Number of accessible bytes starting at
//                                      "input" and going forwards.
//   size_t input_backwards_size      - Number of accessible bytes starting at
//                                      "input" and going backwards

    size_t input_forwards_size,
    size_t input_backwards_size,
  #define prolog { \
      if ((bytes_matched >= max_bytes_matched) || \
          (character_size == 2 && *(input + 1) != 0)) \
      } \
    }
  #define fail_if_error(e) { \
      switch (e) { \
      } \
    }
    max_bytes_matched = (int) yr_min(input_backwards_size, RE_SCAN_LIMIT);
  else
  {
    max_bytes_matched = (int) yr_min(input_forwards_size, RE_SCAN_LIMIT);
  }
  // character_size is 2 and max_bytes_matched is odd we are ignoring the
          match = _yr_re_is_word_char(input, character_size);
          match = !_yr_re_is_word_char(input, character_size);
          if (bytes_matched == 0 && input_backwards_size < character_size)
          {
          }
          {
          }
          {
            assert(input <  input_data + input_forwards_size);
            assert(input >= input_data - input_backwards_size);

            assert(input - input_incr <  input_data + input_forwards_size);
            assert(input - input_incr >= input_data - input_backwards_size);

            match = _yr_re_is_word_char(input, character_size) != \
                    _yr_re_is_word_char(input - input_incr, character_size);
          }
            kill = input_backwards_size > (size_t) bytes_matched;
            kill = input_backwards_size > 0 || (bytes_matched != 0);
                 input_forwards_size > (size_t) bytes_matched;
    size_t input_forwards_size,
    size_t input_backwards_size,
  int max_bytes_matched;

  max_bytes_matched = flags & RE_FLAGS_BACKWARDS ?
      input_backwards_size :
      input_forwards_size;
    size_t input_forwards_size,
    size_t input_backwards_size,
        offset,
        flags,
        offset,
        flags,
        data_size - offset,
  assert_true_rule_blob(
      "rule test { strings: $a = /\\babc/ wide condition: $a }",
      "a\0b\0c\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /\\babc/ wide condition: $a }",
      "\0a\0b\0c\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /\\babc/ wide condition: $a }",
      "\ta\0b\0c\0");

  assert_false_rule_blob(
      "rule test { strings: $a = /\\babc/ wide condition: $a }",
      "x\0a\0b\0c\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /\\babc/ wide condition: $a }",
      "x\ta\0b\0c\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /abc\\b/ wide condition: $a }",
      "a\0b\0c\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /abc\\b/ wide condition: $a }",
      "a\0b\0c\0\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /abc\\b/ wide condition: $a }",
      "a\0b\0c\0\t");

  assert_false_rule_blob(
      "rule test { strings: $a = /abc\\b/ wide condition: $a }",
      "a\0b\0c\0x\0");

  assert_true_rule_blob(
      "rule test { strings: $a = /abc\\b/ wide condition: $a }",
      "a\0b\0c\0b\t");

  assert_false_rule_blob(
      "rule test { strings: $a = /\\b/ wide condition: $a }",
      "abc");