Esempio n. 1
0
static int _yr_atoms_wide(
    YR_ATOM_LIST_ITEM* atoms,
    YR_ATOM_LIST_ITEM** wide_atoms)
{
  YR_ATOM_LIST_ITEM* atom;
  YR_ATOM_LIST_ITEM* new_atom;

  int i;

  *wide_atoms = NULL;
  atom = atoms;

  while (atom != NULL)
  {
    new_atom = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM));

    if (new_atom == NULL)
      return ERROR_INSUFFICIENT_MEMORY;

    for (i = 0; i < YR_MAX_ATOM_LENGTH; i++)
    {
      new_atom->atom.bytes[i] = 0;
      new_atom->atom.mask[i] = 0xFF;
    }

    for (i = 0; i < atom->atom.length; i++)
    {
      if (i * 2 < YR_MAX_ATOM_LENGTH)
        new_atom->atom.bytes[i * 2] = atom->atom.bytes[i];
      else
        break;
    }

    new_atom->atom.length = yr_min(atom->atom.length * 2, YR_MAX_ATOM_LENGTH);
    new_atom->forward_code = atom->forward_code;
    new_atom->backward_code = atom->backward_code;
    new_atom->backtrack = atom->backtrack * 2;
    new_atom->next = *wide_atoms;

    *wide_atoms = new_atom;

    atom = atom->next;
  }

  return ERROR_SUCCESS;
}
Esempio n. 2
0
static int _yr_atoms_xor(
    YR_ATOM_LIST_ITEM* atoms,
    YR_ATOM_LIST_ITEM** xor_atoms)
{
  YR_ATOM_LIST_ITEM* atom;
  YR_ATOM_LIST_ITEM* new_atom;

  int i, j;
  *xor_atoms = NULL;
  atom = atoms;

  while (atom != NULL)
  {
    for (j = 1; j <= 255; j++)
    {
      new_atom = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM));

      if (new_atom == NULL)
        return ERROR_INSUFFICIENT_MEMORY;

      for (i = 0; i < atom->atom.length; i++)
      {
        new_atom->atom.bytes[i] = atom->atom.bytes[i] ^ j;
        new_atom->atom.mask[i] = 0xFF;
      }

      new_atom->atom.length = yr_min(atom->atom.length, YR_MAX_ATOM_LENGTH);
      new_atom->forward_code = atom->forward_code;
      new_atom->backward_code = atom->backward_code;
      new_atom->backtrack = atom->backtrack;
      new_atom->next = *xor_atoms;

      *xor_atoms = new_atom;
    }

    atom = atom->next;
  }
  return ERROR_SUCCESS;
}
Esempio n. 3
0
File: scan.c Progetto: h4rry/yara
int _yr_scan_match_callback(
    uint8_t* match_data,
    int32_t match_length,
    int flags,
    void* args)
{
  CALLBACK_ARGS* callback_args = (CALLBACK_ARGS*) args;

  YR_STRING* string = callback_args->string;
  YR_MATCH* new_match;

  int result = ERROR_SUCCESS;
  int tidx = callback_args->context->tidx;

  size_t match_offset = match_data - callback_args->data;

  // total match length is the sum of backward and forward matches.
  match_length += callback_args->forward_matches;

  if (callback_args->full_word)
  {
    if (flags & RE_FLAGS_WIDE)
    {
      if (match_offset >= 2 &&
          *(match_data - 1) == 0 &&
          isalnum(*(match_data - 2)))
        return ERROR_SUCCESS;

      if (match_offset + match_length + 1 < callback_args->data_size &&
          *(match_data + match_length + 1) == 0 &&
          isalnum(*(match_data + match_length)))
        return ERROR_SUCCESS;
    }
    else
    {
      if (match_offset >= 1 &&
          isalnum(*(match_data - 1)))
        return ERROR_SUCCESS;

      if (match_offset + match_length < callback_args->data_size &&
          isalnum(*(match_data + match_length)))
        return ERROR_SUCCESS;
    }
  }

  if (STRING_IS_CHAIN_PART(string))
  {
    result = _yr_scan_verify_chained_string_match(
        string,
        callback_args->context,
        match_data,
        callback_args->data_base,
        match_offset,
        match_length);
  }
  else
  {
    if (string->matches[tidx].count == 0)
    {
      // If this is the first match for the string, put the string in the
      // list of strings whose flags needs to be cleared after the scan.

      FAIL_ON_ERROR(yr_arena_write_data(
          callback_args->context->matching_strings_arena,
          &string,
          sizeof(string),
          NULL));
    }

    FAIL_ON_ERROR(yr_arena_allocate_memory(
        callback_args->context->matches_arena,
        sizeof(YR_MATCH),
        (void**) &new_match));

    new_match->data_length = yr_min(match_length, MAX_MATCH_DATA);

    FAIL_ON_ERROR(yr_arena_write_data(
        callback_args->context->matches_arena,
        match_data,
        new_match->data_length,
        (void**) &new_match->data));

    if (result == ERROR_SUCCESS)
    {
      new_match->base = callback_args->data_base;
      new_match->offset = match_offset;
      new_match->match_length = match_length;
      new_match->prev = NULL;
      new_match->next = NULL;

      FAIL_ON_ERROR(_yr_scan_add_match_to_list(
          new_match,
          &string->matches[tidx],
          STRING_IS_GREEDY_REGEXP(string)));
    }
  }

  return result;
}
Esempio n. 4
0
File: scan.c Progetto: h4rry/yara
int _yr_scan_verify_chained_string_match(
    YR_STRING* matching_string,
    YR_SCAN_CONTEXT* context,
    uint8_t* match_data,
    uint64_t match_base,
    uint64_t match_offset,
    int32_t match_length)
{
  YR_STRING* string;
  YR_MATCH* match;
  YR_MATCH* next_match;
  YR_MATCH* new_match;

  uint64_t lower_offset;
  uint64_t ending_offset;
  int32_t full_chain_length;

  int tidx = context->tidx;
  int add_match = FALSE;

  if (matching_string->chained_to == NULL)
  {
    add_match = TRUE;
  }
  else
  {
    if (matching_string->unconfirmed_matches[tidx].head != NULL)
      lower_offset = matching_string->unconfirmed_matches[tidx].head->offset;
    else
      lower_offset = match_offset;

    match = matching_string->chained_to->unconfirmed_matches[tidx].head;

    while (match != NULL)
    {
      next_match = match->next;
      ending_offset = match->offset + match->match_length;

      if (ending_offset + matching_string->chain_gap_max < lower_offset)
      {
        _yr_scan_remove_match_from_list(
            match, &matching_string->chained_to->unconfirmed_matches[tidx]);
      }
      else
      {
        if (ending_offset + matching_string->chain_gap_max >= match_offset &&
            ending_offset + matching_string->chain_gap_min <= match_offset)
        {
          add_match = TRUE;
          break;
        }
      }

      match = next_match;
    }
  }

  if (add_match)
  {
    if (STRING_IS_CHAIN_TAIL(matching_string))
    {
      // Chain tails must be chained to some other string
      assert(matching_string->chained_to != NULL);

      match = matching_string->chained_to->unconfirmed_matches[tidx].head;

      while (match != NULL)
      {
        ending_offset = match->offset + match->match_length;

        if (ending_offset + matching_string->chain_gap_max >= match_offset &&
            ending_offset + matching_string->chain_gap_min <= match_offset)
        {
          _yr_scan_update_match_chain_length(
              tidx, matching_string->chained_to, match, 1);
        }

        match = match->next;
      }

      full_chain_length = 0;
      string = matching_string;

      while(string->chained_to != NULL)
      {
        full_chain_length++;
        string = string->chained_to;
      }

      // "string" points now to the head of the strings chain

      match = string->unconfirmed_matches[tidx].head;

      while (match != NULL)
      {
        next_match = match->next;

        if (match->chain_length == full_chain_length)
        {
          _yr_scan_remove_match_from_list(
              match, &string->unconfirmed_matches[tidx]);

          match->match_length = (int32_t) \
              (match_offset - match->offset + match_length);

          match->data_length = yr_min(match->match_length, MAX_MATCH_DATA);

          FAIL_ON_ERROR(yr_arena_write_data(
              context->matches_arena,
              match_data - match_offset + match->offset,
              match->data_length,
              (void**) &match->data));

          FAIL_ON_ERROR(_yr_scan_add_match_to_list(
              match, &string->matches[tidx], FALSE));
        }

        match = next_match;
      }
    }
    else
    {
      if (matching_string->matches[tidx].count == 0 &&
          matching_string->unconfirmed_matches[tidx].count == 0)
      {
        // If this is the first match for the string, put the string in the
        // list of strings whose flags needs to be cleared after the scan.

        FAIL_ON_ERROR(yr_arena_write_data(
            context->matching_strings_arena,
            &matching_string,
            sizeof(matching_string),
            NULL));
      }

      FAIL_ON_ERROR(yr_arena_allocate_memory(
          context->matches_arena,
          sizeof(YR_MATCH),
          (void**) &new_match));

      new_match->data_length = yr_min(match_length, MAX_MATCH_DATA);

      FAIL_ON_ERROR(yr_arena_write_data(
          context->matches_arena,
          match_data,
          new_match->data_length,
          (void**) &new_match->data));

      new_match->base = match_base;
      new_match->offset = match_offset;
      new_match->match_length = match_length;
      new_match->chain_length = 0;
      new_match->prev = NULL;
      new_match->next = NULL;

      FAIL_ON_ERROR(_yr_scan_add_match_to_list(
          new_match,
          &matching_string->unconfirmed_matches[tidx],
          FALSE));
    }
  }

  return ERROR_SUCCESS;
}
Esempio n. 5
0
File: re.c Progetto: ewil/yara
int yr_re_exec(
    RE_CODE re_code,
    uint8_t* input_data,
    size_t input_size,
    int flags,
    RE_MATCH_CALLBACK_FUNC callback,
    void* callback_args)
{
  uint8_t* input;
  uint8_t mask;
  uint8_t value;

  RE_CODE ip;
  RE_FIBER_LIST fibers;
  RE_THREAD_STORAGE* storage;
  RE_FIBER* fiber;
  RE_FIBER* next_fiber;

  int error;
  int count;
  int max_count;
  int match;
  int character_size;
  int input_incr;
  int kill;
  int action;
  int result = -1;

  #define ACTION_NONE       0
  #define ACTION_CONTINUE   1
  #define ACTION_KILL       2
  #define ACTION_KILL_TAIL  3

  #define prolog if (count >= max_count) \
      { \
        action = ACTION_KILL; \
        break; \
      }

  #define fail_if_error(e) switch (e) { \
        case ERROR_INSUFICIENT_MEMORY: \
          return -2; \
        case ERROR_TOO_MANY_RE_FIBERS: \
          return -4; \
      }

  if (_yr_re_alloc_storage(&storage) != ERROR_SUCCESS)
    return -2;

  if (flags & RE_FLAGS_WIDE)
    character_size = 2;
  else
    character_size = 1;

  input = input_data;
  input_incr = character_size;

  if (flags & RE_FLAGS_BACKWARDS)
  {
    input -= character_size;
    input_incr = -input_incr;
  }

  max_count = (int) yr_min(input_size, RE_SCAN_LIMIT);

  // Round down max_count to a multiple of character_size, this way if
  // character_size is 2 and input_size is odd we are ignoring the
  // extra byte which can't match anyways.

  max_count = max_count - max_count % character_size;
  count = 0;

  error = _yr_re_fiber_create(&storage->fiber_pool, &fiber);
  fail_if_error(error);

  fiber->ip = re_code;
  fibers.head = fiber;
  fibers.tail = fiber;

  error = _yr_re_fiber_sync(&fibers, &storage->fiber_pool, fiber);
  fail_if_error(error);

  while (fibers.head != NULL)
  {
    fiber = fibers.head;

    while(fiber != NULL)
    {
      ip = fiber->ip;
      action = ACTION_NONE;

      switch(*ip)
      {
        case RE_OPCODE_ANY:
          prolog;
          action = ACTION_NONE;
          fiber->ip += 1;
          break;

        case RE_OPCODE_ANY_EXCEPT_NEW_LINE:
          prolog;
          match = (*input != 0x0A);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 1;
          break;

        case RE_OPCODE_LITERAL:
          prolog;
          match = (*input == *(ip + 1));
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 2;
          break;

        case RE_OPCODE_LITERAL_NO_CASE:
          prolog;
          match = lowercase[*input] == lowercase[*(ip + 1)];
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 2;
          break;

        case RE_OPCODE_MASKED_LITERAL:
          prolog;
          value = *(int16_t*)(ip + 1) & 0xFF;
          mask = *(int16_t*)(ip + 1) >> 8;

          // We don't need to take into account the case-insensitive
          // case because this opcode is only used with hex strings,
          // which can't be case-insensitive.

          match = ((*input & mask) == value);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 3;
          break;

        case RE_OPCODE_CLASS:
          prolog;
          match = CHAR_IN_CLASS(*input, ip + 1);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 33;
          break;

        case RE_OPCODE_CLASS_NO_CASE:
          prolog;
          match = CHAR_IN_CLASS(*input, ip + 1) ||
                  CHAR_IN_CLASS(altercase[*input], ip + 1);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 33;
          break;

        case RE_OPCODE_WORD_CHAR:
          prolog;
          match = IS_WORD_CHAR(*input);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 1;
          break;

        case RE_OPCODE_NON_WORD_CHAR:
          prolog;
          match = !IS_WORD_CHAR(*input);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 1;
          break;

        case RE_OPCODE_SPACE:
        case RE_OPCODE_NON_SPACE:
          prolog;

          switch(*input)
          {
            case ' ':
            case '\t':
            case '\r':
            case '\n':
            case '\v':
            case '\f':
              match = TRUE;
              break;

            default:
              match = FALSE;
          }

          if (*ip == RE_OPCODE_NON_SPACE)
            match = !match;

          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 1;
          break;

        case RE_OPCODE_DIGIT:
          prolog;
          match = isdigit(*input);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 1;
          break;

        case RE_OPCODE_NON_DIGIT:
          prolog;
          match = !isdigit(*input);
          action = match ? ACTION_NONE : ACTION_KILL;
          fiber->ip += 1;
          break;

        case RE_OPCODE_WORD_BOUNDARY:
        case RE_OPCODE_NON_WORD_BOUNDARY:

          if (count == 0 &&
              !(flags & RE_FLAGS_NOT_AT_START) &&
              !(flags & RE_FLAGS_BACKWARDS))
            match = TRUE;
          else if (count >= max_count)
            match = TRUE;
          else if (IS_WORD_CHAR(*(input - input_incr)) != IS_WORD_CHAR(*input))
            match = TRUE;
          else
            match = FALSE;

          if (*ip == RE_OPCODE_NON_WORD_BOUNDARY)
            match = !match;

          action = match ? ACTION_CONTINUE : ACTION_KILL;
          break;

        case RE_OPCODE_MATCH_AT_START:
          if (flags & RE_FLAGS_BACKWARDS)
            kill = input_size > (size_t) count;
          else
            kill = (flags & RE_FLAGS_NOT_AT_START) || (count != 0);
          action = kill ? ACTION_KILL : ACTION_CONTINUE;
          break;

        case RE_OPCODE_MATCH_AT_END:
          action = input_size > (size_t) count ? ACTION_KILL : ACTION_CONTINUE;
          break;

        case RE_OPCODE_MATCH:
          result = count;

          if (flags & RE_FLAGS_EXHAUSTIVE)
          {
            if (callback != NULL)
            {
              int cb_result;

              if (flags & RE_FLAGS_BACKWARDS)
                cb_result = callback(
                    input + character_size, count, flags, callback_args);
              else
                cb_result = callback(
                    input_data, count, flags, callback_args);

              switch(cb_result)
              {
                case ERROR_INSUFICIENT_MEMORY:
                  return -2;
                case ERROR_TOO_MANY_MATCHES:
                  return -3;
                default:
                  if (cb_result != ERROR_SUCCESS)
                    return -4;
              }
            }

            action = ACTION_KILL;
          }
          else
          {
            action = ACTION_KILL_TAIL;
          }

          break;

        default:
          assert(FALSE);
      }

      switch(action)
      {
        case ACTION_KILL:
          fiber = _yr_re_fiber_kill(&fibers, &storage->fiber_pool, fiber);
          break;

        case ACTION_KILL_TAIL:
          _yr_re_fiber_kill_tail(&fibers, &storage->fiber_pool, fiber);
          fiber = NULL;
          break;

        case ACTION_CONTINUE:
          fiber->ip += 1;
          error = _yr_re_fiber_sync(&fibers, &storage->fiber_pool, fiber);
          fail_if_error(error);
          break;

        default:
          next_fiber = fiber->next;
          error = _yr_re_fiber_sync(&fibers, &storage->fiber_pool, fiber);
          fail_if_error(error);
          fiber = next_fiber;
      }
    }

    if (flags & RE_FLAGS_WIDE && count < max_count && *(input + 1) != 0)
      _yr_re_fiber_kill_all(&fibers, &storage->fiber_pool);

    input += input_incr;
    count += character_size;

    if (flags & RE_FLAGS_SCAN && count < max_count)
    {
      error = _yr_re_fiber_create(&storage->fiber_pool, &fiber);
      fail_if_error(error);

      fiber->ip = re_code;
      _yr_re_fiber_append(&fibers, fiber);

      error = _yr_re_fiber_sync(&fibers, &storage->fiber_pool, fiber);
      fail_if_error(error);
    }
  }

  return result;
}
Esempio n. 6
0
int64_t pe_rva_to_offset(
    PE* pe,
    uint64_t rva)
{
  PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pe->header);

  DWORD lowest_section_rva = 0xffffffff;
  DWORD section_rva = 0;
  DWORD section_offset = 0;
  DWORD section_raw_size = 0;

  int64_t result;

  int i = 0;

  int alignment = 0;
  int rest = 0;

  while(i < yr_min(yr_le16toh(pe->header->FileHeader.NumberOfSections), MAX_PE_SECTIONS))
  {
    if (struct_fits_in_pe(pe, section, IMAGE_SECTION_HEADER))
    {
      if (lowest_section_rva > yr_le32toh(section->VirtualAddress))
      {
        lowest_section_rva = yr_le32toh(section->VirtualAddress);
      }

      if (rva >= yr_le32toh(section->VirtualAddress) &&
          section_rva <= yr_le32toh(section->VirtualAddress))
      {
        // Round section_offset
        //
        // Rounding everything less than 0x200 to 0 as discussed in
        // https://code.google.com/archive/p/corkami/wikis/PE.wiki#PointerToRawData
        // does not work for PE32_FILE from the test suite and for
        // some tinype samples where File Alignment = 4
        // (http://www.phreedom.org/research/tinype/).
        //
        // If FileAlignment is >= 0x200, it is apparently ignored (see
        // Ero Carreras's pefile.py, PE.adjust_FileAlignment).

        alignment = yr_min(yr_le32toh(OptionalHeader(pe, FileAlignment)), 0x200);

        section_rva = yr_le32toh(section->VirtualAddress);
        section_offset = yr_le32toh(section->PointerToRawData);
        section_raw_size = yr_le32toh(section->SizeOfRawData);

        if (alignment)
        {
          rest = section_offset % alignment;

          if (rest)
            section_offset -= rest;
        }
      }

      section++;
      i++;
    }
    else
    {
      return -1;
    }
  }

  // Everything before the first section seems to get mapped straight
  // relative to ImageBase.

  if (rva < lowest_section_rva)
  {
    section_rva = 0;
    section_offset = 0;
    section_raw_size = (DWORD) pe->data_size;
  }

  // Many sections, have a raw (on disk) size smaller than their in-memory size.
  // Check for rva's that map to this sparse space, and therefore have no valid
  // associated file offset.

  if ((rva - section_rva) >= section_raw_size)
    return -1;

  result = section_offset + (rva - section_rva);

  // Check that the offset fits within the file.
  if (result >= pe->data_size)
    return -1;

  return result;
}
Esempio n. 7
0
void _yr_ac_print_automaton_state(
  YR_AC_STATE* state)
{
  int i;
  int child_count;

  YR_AC_MATCH* match;
  YR_AC_STATE* child_state;

  for (i = 0; i < state->depth; i++)
    printf(" ");

  child_state = state->first_child;
  child_count = 0;

  while(child_state != NULL)
  {
    child_count++;
    child_state = child_state->siblings;
  }

  printf("%p childs:%d depth:%d failure:%p",
         state, child_count, state->depth, state->failure);

  match = state->matches;

  while (match != NULL)
  {
    printf("\n");

    for (i = 0; i < state->depth + 1; i++)
      printf(" ");

    printf("%s = ", match->string->identifier);

    if (STRING_IS_HEX(match->string))
    {
      printf("{ ");

      for (i = 0; i < yr_min(match->string->length, 10); i++)
        printf("%02x ", match->string->string[i]);

      printf("}");
    }
    else if (STRING_IS_REGEXP(match->string))
    {
      printf("/");

      for (i = 0; i < yr_min(match->string->length, 10); i++)
        printf("%c", match->string->string[i]);

      printf("/");
    }
    else
    {
      printf("\"");

      for (i = 0; i < yr_min(match->string->length, 10); i++)
        printf("%c", match->string->string[i]);

      printf("\"");
    }

    match = match->next;
  }

  printf("\n");

  child_state = state->first_child;

  while(child_state != NULL)
  {
    _yr_ac_print_automaton_state(child_state);
    child_state = child_state->siblings;
  }
}
Esempio n. 8
0
static int _yr_atoms_extract_from_re(
    YR_ATOMS_CONFIG* config,
    RE_AST* re_ast,
    YR_ATOM_TREE_NODE* appending_node)
{
  YR_STACK* stack;
  RE_NODE* re_node;

  YR_ATOM atom;
  YR_ATOM best_atom;

  struct STACK_ITEM si;

  int i, shift;
  int quality;
  int best_quality = -1;
  int n = 0;

  YR_ATOM_TREE_NODE* and_node;
  YR_ATOM_TREE_NODE* left_node;
  YR_ATOM_TREE_NODE* right_node;

  // The RE_NODEs most recently visited that can conform an atom (ie:
  // RE_NODE_LITERAL, RE_NODE_MASKED_LITERAL and RE_NODE_ANY). The number of
  // items in this array is n.
  RE_NODE* recent_re_nodes[YR_MAX_ATOM_LENGTH];

  // The RE_NODEs corresponding to the best atom found so far for the current
  // appending node.
  RE_NODE* best_atom_re_nodes[YR_MAX_ATOM_LENGTH];

  // This holds the ATOM_TREE_OR node where leaves (ATOM_TREE_LEAF) are
  // currently being appended.
  YR_ATOM_TREE_NODE* current_appending_node = NULL;

  // This holds the ATOM_TREE_LEAF node whose atom is currently being updated.
  YR_ATOM_TREE_NODE* leaf = NULL;

  FAIL_ON_ERROR(yr_stack_create(1024, sizeof(si), &stack));

  // This first item pushed in the stack is the last one to be poped out, its
  // sole purpose is forcing that any pending
  si.re_node = NULL;
  si.new_appending_node = appending_node;

  FAIL_ON_ERROR_WITH_CLEANUP(
      yr_stack_push(stack, (void*) &si),
      yr_stack_destroy(stack));

  // Start processing the root node.
  si.re_node = re_ast->root_node;

  // Leaf nodes are initially appended to the node passed in the appending_node,
  // argument which is the root ATOM_TREE_OR node that is empty at this point.
  si.new_appending_node = appending_node;

  FAIL_ON_ERROR_WITH_CLEANUP(
      yr_stack_push(stack, (void*) &si),
      yr_stack_destroy(stack));

  while (yr_stack_pop(stack, (void*) &si))
  {
    // Change the appending node if the item poped from the stack says so.
    if (si.new_appending_node != NULL)
    {
      // Before changing the appending node let's append any pending leaf to
      // the current appending node.
      if (n > 0)
      {
        make_atom_from_re_nodes(atom, n, recent_re_nodes);
        shift = _yr_atoms_trim(&atom);
        quality = config->get_atom_quality(config, &atom);

        FAIL_ON_NULL_WITH_CLEANUP(
            leaf = _yr_atoms_tree_node_create(ATOM_TREE_LEAF),
            yr_stack_destroy(stack));

        if (quality > best_quality)
        {
          memcpy(&leaf->atom, &atom, sizeof(atom));
          memcpy(
              &leaf->re_nodes,
              &recent_re_nodes[shift],
              sizeof(recent_re_nodes) - shift * sizeof(recent_re_nodes[0]));
        }
        else
        {
          memcpy(&leaf->atom, &best_atom, sizeof(best_atom));
          memcpy(
              &leaf->re_nodes,
              &best_atom_re_nodes,
              sizeof(best_atom_re_nodes));
        }

        _yr_atoms_tree_node_append(current_appending_node, leaf);
        n = 0;
      }

      current_appending_node = si.new_appending_node;
    }

    if (si.re_node != NULL)
    {
      switch(si.re_node->type)
      {
        case RE_NODE_LITERAL:
        case RE_NODE_MASKED_LITERAL:
        case RE_NODE_ANY:

          if (n < YR_MAX_ATOM_LENGTH)
          {
            recent_re_nodes[n] = si.re_node;
            best_atom_re_nodes[n] = si.re_node;
            best_atom.bytes[n] = (uint8_t) si.re_node->value;
            best_atom.mask[n] = (uint8_t) si.re_node->mask;
            best_atom.length = ++n;
          }
          else if (best_quality < YR_MAX_ATOM_QUALITY)
          {
            make_atom_from_re_nodes(atom, n, recent_re_nodes);
            shift = _yr_atoms_trim(&atom);
            quality = config->get_atom_quality(config, &atom);

            if (quality > best_quality)
            {
              for (i = 0; i < atom.length; i++)
              {
                best_atom.bytes[i] = atom.bytes[i];
                best_atom.mask[i] = atom.mask[i];
                best_atom_re_nodes[i] = recent_re_nodes[i + shift];
              }

              best_atom.length = atom.length;
              best_quality = quality;
            }

            for (i = 1; i < YR_MAX_ATOM_LENGTH; i++)
              recent_re_nodes[i - 1] = recent_re_nodes[i];

            recent_re_nodes[YR_MAX_ATOM_LENGTH - 1] = si.re_node;
          }

          break;

        case RE_NODE_CONCAT:

          re_node = si.re_node->children_tail;

          // Push children right to left, they are poped left to right.
          while (re_node != NULL)
          {
            si.new_appending_node = NULL;
            si.re_node = re_node;

            FAIL_ON_ERROR_WITH_CLEANUP(
                yr_stack_push(stack, &si),
                yr_stack_destroy(stack));

            re_node = re_node->prev_sibling;
          }

          break;

        case RE_NODE_ALT:

          // Create ATOM_TREE_AND node with two ATOM_TREE_OR children nodes.
          and_node = _yr_atoms_tree_node_create(ATOM_TREE_AND);
          left_node = _yr_atoms_tree_node_create(ATOM_TREE_OR);
          right_node = _yr_atoms_tree_node_create(ATOM_TREE_OR);

          if (and_node == NULL || left_node == NULL || right_node == NULL)
          {
            _yr_atoms_tree_node_destroy(and_node);
            _yr_atoms_tree_node_destroy(left_node);
            _yr_atoms_tree_node_destroy(right_node);

            yr_stack_destroy(stack);

            return ERROR_INSUFFICIENT_MEMORY;
          }

          and_node->children_head = left_node;
          and_node->children_tail = right_node;
          left_node->next_sibling = right_node;

          // Add the ATOM_TREE_AND as children of the current node.
          _yr_atoms_tree_node_append(current_appending_node, and_node);

          re_node = si.re_node;

          si.new_appending_node = current_appending_node;
          si.re_node = NULL;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          // RE_NODE_ALT nodes has only two children, so children_head is the
          // left one, and children_tail is right one.
          si.new_appending_node = right_node;
          si.re_node = re_node->children_tail;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          si.new_appending_node = left_node;
          si.re_node = re_node->children_head;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          break;

        case RE_NODE_PLUS:

          re_node = si.re_node;

          si.new_appending_node = current_appending_node;
          si.re_node = NULL;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          si.new_appending_node = NULL;
          // RE_NODE_PLUS nodes has a single child, which is children_head.
          si.re_node = re_node->children_head;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          break;

        case RE_NODE_RANGE:

          re_node = si.re_node;

          si.new_appending_node = current_appending_node;
          si.re_node = NULL;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          si.new_appending_node = NULL;
          // RE_NODE_RANGE nodes has a single child, which is children_head.
          si.re_node = re_node->children_head;

          // In a regexp like /a{10,20}/ the optimal atom is 'aaaa' (assuming
          // that YR_MAX_ATOM_LENGTH = 4) because the 'a' character must appear
          // at least 10 times in the matching string. Each call in the loop
          // will append one 'a' to the atom, so YR_MAX_ATOM_LENGTH iterations
          // are enough.

          for (i = 0; i < yr_min(re_node->start, YR_MAX_ATOM_LENGTH); i++)
          {
            FAIL_ON_ERROR_WITH_CLEANUP(
                yr_stack_push(stack, &si),
                yr_stack_destroy(stack));
          }

          break;

        case RE_NODE_RANGE_ANY:
        case RE_NODE_STAR:
        case RE_NODE_CLASS:
        case RE_NODE_WORD_CHAR:
        case RE_NODE_NON_WORD_CHAR:
        case RE_NODE_SPACE:
        case RE_NODE_NON_SPACE:
        case RE_NODE_DIGIT:
        case RE_NODE_NON_DIGIT:
        case RE_NODE_EMPTY:
        case RE_NODE_ANCHOR_START:
        case RE_NODE_ANCHOR_END:
        case RE_NODE_WORD_BOUNDARY:
        case RE_NODE_NON_WORD_BOUNDARY:

          si.new_appending_node = current_appending_node;
          si.re_node = NULL;

          FAIL_ON_ERROR_WITH_CLEANUP(
              yr_stack_push(stack, &si),
              yr_stack_destroy(stack));

          break;

        default:
          assert(false);
      }
    }
  }

  yr_stack_destroy(stack);

  return ERROR_SUCCESS;
}