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; }
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; }
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; }
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; }
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; }
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; }
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; } }
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; }