mirror of
https://github.com/VirusTotal/yara.git
synced 2026-10-12 02:10:00 +00:00
Replace yr_re_fast_exec with a faster implementation. (#1526)
yr_re_fast_exec used a very naive approach based on backtracking which had extremely bad performance with certain pathological cases. For example the following pattern could take more than a minute when applied to a small file containing the sequence 0xAA 0xAA 0xAA 0xAA three times, with around 100 zeroes following each of those sequences:
{ AA AA AA AA [1-60] 00 [1-60] 00 [1-60] AA AA AA AA [1-60] 00 [1-60] 00 [1-60] AA AA AA AA [1-60] 00 [1-60] BB }
With the new implementation it takes a fraction of a second.
This commit is contained in:
@@ -94,9 +94,6 @@ matrix:
|
||||
before_script: ./bootstrap.sh
|
||||
|
||||
script: |
|
||||
rvm get head # Workaround for Travis CI issue https://github.com/travis-ci/travis-ci/issues/6307
|
||||
set -e
|
||||
unset CC # A pre-set CC overrides --host settings.
|
||||
./configure $CONFIGFLAGS
|
||||
make clean && make
|
||||
case "$CONFIGFLAGS" in
|
||||
|
||||
@@ -124,12 +124,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#define YR_MAX_OVERLOADED_FUNCTIONS 10
|
||||
#endif
|
||||
|
||||
// Size of the stack used by yr_re_fast_exec.
|
||||
#ifndef YR_MAX_FAST_RE_STACK
|
||||
#define YR_MAX_FAST_RE_STACK 300
|
||||
#endif
|
||||
|
||||
|
||||
// Regular expressions like /foo.{x,y}bar/ are split in two separate ones /foo/
|
||||
// and /bar/ if x is larger than YR_STRING_CHAINING_THRESHOLD. This also applies
|
||||
// to hex strings like { 01 02 03 [x-y] 004 05 06 }.
|
||||
|
||||
@@ -159,6 +159,9 @@ typedef struct RE_ERROR RE_ERROR;
|
||||
typedef struct RE_FIBER RE_FIBER;
|
||||
typedef struct RE_FIBER_LIST RE_FIBER_LIST;
|
||||
typedef struct RE_FIBER_POOL RE_FIBER_POOL;
|
||||
typedef struct RE_FAST_EXEC_POSITION RE_FAST_EXEC_POSITION;
|
||||
typedef struct RE_FAST_EXEC_POSITION_LIST RE_FAST_EXEC_POSITION_LIST;
|
||||
typedef struct RE_FAST_EXEC_POSITION_POOL RE_FAST_EXEC_POSITION_POOL;
|
||||
|
||||
typedef struct YR_AC_STATE YR_AC_STATE;
|
||||
typedef struct YR_AC_AUTOMATON YR_AC_AUTOMATON;
|
||||
@@ -433,6 +436,19 @@ struct RE_FIBER_POOL
|
||||
RE_FIBER_LIST fibers;
|
||||
};
|
||||
|
||||
struct RE_FAST_EXEC_POSITION
|
||||
{
|
||||
int round;
|
||||
const uint8_t* input;
|
||||
RE_FAST_EXEC_POSITION* prev;
|
||||
RE_FAST_EXEC_POSITION* next;
|
||||
};
|
||||
|
||||
struct RE_FAST_EXEC_POSITION_POOL
|
||||
{
|
||||
RE_FAST_EXEC_POSITION* head;
|
||||
};
|
||||
|
||||
struct YR_MODIFIER
|
||||
{
|
||||
int32_t flags;
|
||||
@@ -760,6 +776,9 @@ struct YR_SCAN_CONTEXT
|
||||
// Fiber pool used by yr_re_exec.
|
||||
RE_FIBER_POOL re_fiber_pool;
|
||||
|
||||
// Pool used by yr_re_fast_exec.
|
||||
RE_FAST_EXEC_POSITION_POOL re_fast_exec_position_pool;
|
||||
|
||||
// A bitmap with one bit per rule, bit N is set when the rule with index N
|
||||
// has matched.
|
||||
YR_BITMASK* rule_matches_flags;
|
||||
|
||||
+84
-75
@@ -519,6 +519,63 @@ static int _yr_parser_write_string(
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _yr_parser_check_string_modifiers(
|
||||
yyscan_t yyscanner,
|
||||
YR_MODIFIER modifier)
|
||||
{
|
||||
YR_COMPILER* compiler = yyget_extra(yyscanner);
|
||||
|
||||
// xor and nocase together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_XOR &&
|
||||
modifier.flags & STRING_FLAGS_NO_CASE)
|
||||
{
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler, "invalid modifier combination: xor nocase");
|
||||
return ERROR_INVALID_MODIFIER;
|
||||
}
|
||||
|
||||
// base64 and nocase together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_NO_CASE &&
|
||||
(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
modifier.flags & STRING_FLAGS_BASE64_WIDE))
|
||||
{
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
modifier.flags & STRING_FLAGS_BASE64
|
||||
? "invalid modifier combination: base64 nocase"
|
||||
: "invalid modifier combination: base64wide nocase");
|
||||
return ERROR_INVALID_MODIFIER;
|
||||
}
|
||||
|
||||
// base64 and fullword together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_FULL_WORD &&
|
||||
(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
modifier.flags & STRING_FLAGS_BASE64_WIDE))
|
||||
{
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
modifier.flags & STRING_FLAGS_BASE64
|
||||
? "invalid modifier combination: base64 fullword"
|
||||
: "invalid modifier combination: base64wide fullword");
|
||||
return ERROR_INVALID_MODIFIER;
|
||||
}
|
||||
|
||||
// base64 and xor together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_XOR &&
|
||||
(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
modifier.flags & STRING_FLAGS_BASE64_WIDE))
|
||||
{
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
modifier.flags & STRING_FLAGS_BASE64
|
||||
? "invalid modifier combination: base64 xor"
|
||||
: "invalid modifier combination: base64wide xor");
|
||||
return ERROR_INVALID_MODIFIER;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int yr_parser_reduce_string_declaration(
|
||||
yyscan_t yyscanner,
|
||||
YR_MODIFIER modifier,
|
||||
@@ -552,33 +609,15 @@ int yr_parser_reduce_string_declaration(
|
||||
// The string was already defined, return an error.
|
||||
if (string_idx != UINT32_MAX)
|
||||
{
|
||||
result = ERROR_DUPLICATED_STRING_IDENTIFIER;
|
||||
yr_compiler_set_error_extra_info(compiler, identifier) goto _exit;
|
||||
yr_compiler_set_error_extra_info(compiler, identifier);
|
||||
return ERROR_DUPLICATED_STRING_IDENTIFIER;
|
||||
}
|
||||
|
||||
// Empty strings are not allowed.
|
||||
if (str->length == 0)
|
||||
{
|
||||
result = ERROR_EMPTY_STRING;
|
||||
yr_compiler_set_error_extra_info(compiler, identifier) goto _exit;
|
||||
}
|
||||
|
||||
// If string identifier is $ this is an anonymous string, if not add the
|
||||
// identifier to strings_table.
|
||||
if (strcmp(identifier, "$") == 0)
|
||||
{
|
||||
modifier.flags |= STRING_FLAGS_ANONYMOUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = yr_hash_table_add_uint32(
|
||||
compiler->strings_table,
|
||||
identifier,
|
||||
NULL,
|
||||
compiler->current_string_idx);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
goto _exit;
|
||||
yr_compiler_set_error_extra_info(compiler, identifier);
|
||||
return ERROR_EMPTY_STRING;
|
||||
}
|
||||
|
||||
if (str->flags & SIZED_STRING_FLAGS_NO_CASE)
|
||||
@@ -591,54 +630,6 @@ int yr_parser_reduce_string_declaration(
|
||||
if (modifier.flags & STRING_FLAGS_HEXADECIMAL)
|
||||
modifier.flags |= STRING_FLAGS_DOT_ALL;
|
||||
|
||||
// xor and nocase together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_XOR &&
|
||||
modifier.flags & STRING_FLAGS_NO_CASE)
|
||||
{
|
||||
result = ERROR_INVALID_MODIFIER;
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler, "invalid modifier combination: xor nocase") goto _exit;
|
||||
}
|
||||
|
||||
// base64 and nocase together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_NO_CASE &&
|
||||
(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
modifier.flags & STRING_FLAGS_BASE64_WIDE))
|
||||
{
|
||||
result = ERROR_INVALID_MODIFIER;
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
modifier.flags & STRING_FLAGS_BASE64
|
||||
? "invalid modifier combination: base64 nocase"
|
||||
: "invalid modifier combination: base64wide nocase") goto _exit;
|
||||
}
|
||||
|
||||
// base64 and fullword together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_FULL_WORD &&
|
||||
(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
modifier.flags & STRING_FLAGS_BASE64_WIDE))
|
||||
{
|
||||
result = ERROR_INVALID_MODIFIER;
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
modifier.flags & STRING_FLAGS_BASE64
|
||||
? "invalid modifier combination: base64 fullword"
|
||||
: "invalid modifier combination: base64wide fullword") goto _exit;
|
||||
}
|
||||
|
||||
// base64 and xor together is not implemented.
|
||||
if (modifier.flags & STRING_FLAGS_XOR &&
|
||||
(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
modifier.flags & STRING_FLAGS_BASE64_WIDE))
|
||||
{
|
||||
result = ERROR_INVALID_MODIFIER;
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
modifier.flags & STRING_FLAGS_BASE64
|
||||
? "invalid modifier combination: base64 xor"
|
||||
: "invalid modifier combination: base64wide xor") goto _exit;
|
||||
}
|
||||
|
||||
if (!(modifier.flags & STRING_FLAGS_WIDE) &&
|
||||
!(modifier.flags & STRING_FLAGS_XOR) &&
|
||||
!(modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
@@ -661,6 +652,25 @@ int yr_parser_reduce_string_declaration(
|
||||
// and unmarked later if required.
|
||||
modifier.flags |= STRING_FLAGS_FIXED_OFFSET;
|
||||
|
||||
// If string identifier is $ this is an anonymous string, if not add the
|
||||
// identifier to strings_table.
|
||||
if (strcmp(identifier, "$") == 0)
|
||||
{
|
||||
modifier.flags |= STRING_FLAGS_ANONYMOUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL_ON_ERROR(yr_hash_table_add_uint32(
|
||||
compiler->strings_table,
|
||||
identifier,
|
||||
NULL,
|
||||
compiler->current_string_idx));
|
||||
}
|
||||
|
||||
// Make sure that the the string does not have an invalid combination of
|
||||
// modifiers.
|
||||
FAIL_ON_ERROR(_yr_parser_check_string_modifiers(yyscanner, modifier));
|
||||
|
||||
if (modifier.flags & STRING_FLAGS_HEXADECIMAL ||
|
||||
modifier.flags & STRING_FLAGS_REGEXP ||
|
||||
modifier.flags & STRING_FLAGS_BASE64 ||
|
||||
@@ -684,9 +694,8 @@ int yr_parser_reduce_string_declaration(
|
||||
identifier,
|
||||
re_error.message);
|
||||
|
||||
yr_compiler_set_error_extra_info(compiler, message)
|
||||
|
||||
goto _exit;
|
||||
yr_compiler_set_error_extra_info(compiler, message);
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
if (re_ast->flags & RE_FLAGS_FAST_REGEXP)
|
||||
@@ -709,9 +718,9 @@ int yr_parser_reduce_string_declaration(
|
||||
yr_compiler_set_error_extra_info(
|
||||
compiler,
|
||||
"greedy and ungreedy quantifiers can't be mixed in a regular "
|
||||
"expression")
|
||||
"expression");
|
||||
|
||||
goto _exit;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
if (yr_re_ast_has_unbounded_quantifier_for_dot(re_ast))
|
||||
|
||||
+321
-133
@@ -1988,14 +1988,82 @@ int yr_re_exec(
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This function replaces yr_re_exec for regular expressions marked with flag
|
||||
// RE_FLAGS_FAST_REGEXP. These are regular expression whose code contain only
|
||||
// the following operations: RE_OPCODE_LITERAL, RE_OPCODE_MASKED_LITERAL,
|
||||
// RE_OPCODE_ANY, RE_OPCODE_REPEAT_ANY_UNGREEDY and RE_OPCODE_MATCH. Some
|
||||
// examples of regular expressions that can be executed with this function are:
|
||||
// Helper function that creates a RE_FAST_EXEC_POSITION by either allocating it
|
||||
// or reusing a previously allocated one from a pool.
|
||||
//
|
||||
// /foobar/
|
||||
// /foo.*?bar/
|
||||
static int _yr_re_fast_exec_position_create(
|
||||
RE_FAST_EXEC_POSITION_POOL* pool,
|
||||
RE_FAST_EXEC_POSITION** new_position)
|
||||
{
|
||||
RE_FAST_EXEC_POSITION* position;
|
||||
|
||||
if (pool->head != NULL)
|
||||
{
|
||||
position = pool->head;
|
||||
pool->head = position->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = (RE_FAST_EXEC_POSITION*) yr_malloc(
|
||||
sizeof(RE_FAST_EXEC_POSITION));
|
||||
|
||||
if (position == NULL)
|
||||
return ERROR_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
|
||||
position->input = NULL;
|
||||
position->round = 0;
|
||||
position->next = NULL;
|
||||
position->prev = NULL;
|
||||
|
||||
*new_position = position;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper function that moves a list of RE_FAST_EXEC_POSITION structures to a
|
||||
// pool represented by a RE_FAST_EXEC_POSITION_POOL. Receives pointers to the
|
||||
// pool, the first item, and last item in the list.
|
||||
//
|
||||
static void _yr_re_fast_exec_destroy_position_list(
|
||||
RE_FAST_EXEC_POSITION_POOL* pool,
|
||||
RE_FAST_EXEC_POSITION* first,
|
||||
RE_FAST_EXEC_POSITION* last)
|
||||
{
|
||||
last->next = pool->head;
|
||||
|
||||
if (pool->head != NULL)
|
||||
pool->head->prev = last;
|
||||
|
||||
pool->head = first;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This function replaces yr_re_exec for regular expressions marked with flag
|
||||
// RE_FLAGS_FAST_REGEXP. These regular expressions are derived from hex strings
|
||||
// that don't contain alternatives, like for example:
|
||||
//
|
||||
// { 01 02 03 04 [0-2] 04 05 06 07 }
|
||||
//
|
||||
// The regexp's code produced by such strings can be matched by a faster, less
|
||||
// general algorithm, and it contains only the following opcodes:
|
||||
//
|
||||
// * RE_OPCODE_LITERAL
|
||||
// * RE_OPCODE_MASKED_LITERAL,
|
||||
// * RE_OPCODE_ANY
|
||||
// * RE_OPCODE_REPEAT_ANY_UNGREEDY
|
||||
// * RE_OPCODE_MATCH.
|
||||
//
|
||||
// The regexp's code is executed one instruction at time, and the function
|
||||
// maintains a list of positions within the input for tracking different
|
||||
// matching alternatives, these positions are described by an instance of
|
||||
// RE_FAST_EXEC_POSITION. With each instruction the list of positions is
|
||||
// updated, removing those where the input data doesn't match the current
|
||||
// instruction, or adding new positions if the instruction is
|
||||
// RE_OPCODE_REPEAT_ANY_UNGREEDY. The list of positions is maintained sorted
|
||||
// by the value of the pointer they hold to the input, and it doesn't contain
|
||||
// duplicated pointer values.
|
||||
//
|
||||
int yr_re_fast_exec(
|
||||
YR_SCAN_CONTEXT* context,
|
||||
@@ -2010,162 +2078,282 @@ int yr_re_fast_exec(
|
||||
{
|
||||
RE_REPEAT_ANY_ARGS* repeat_any_args;
|
||||
|
||||
const uint8_t* code_stack[YR_MAX_FAST_RE_STACK];
|
||||
const uint8_t* input_stack[YR_MAX_FAST_RE_STACK];
|
||||
int matches_stack[YR_MAX_FAST_RE_STACK];
|
||||
|
||||
const uint8_t* input = input_data;
|
||||
const uint8_t* next_input;
|
||||
const uint8_t* ip = code;
|
||||
const uint8_t* next_opcode;
|
||||
|
||||
uint8_t mask;
|
||||
uint8_t value;
|
||||
|
||||
int stop;
|
||||
int input_incr;
|
||||
int sp = 0;
|
||||
// Pointers to the first and last position in the list.
|
||||
RE_FAST_EXEC_POSITION* first;
|
||||
RE_FAST_EXEC_POSITION* last;
|
||||
|
||||
int input_incr = flags & RE_FLAGS_BACKWARDS ? -1 : 1;
|
||||
int max_bytes_matched = flags & RE_FLAGS_BACKWARDS
|
||||
? (int) input_backwards_size
|
||||
: (int) input_forwards_size;
|
||||
int bytes_matched;
|
||||
int max_bytes_matched;
|
||||
|
||||
max_bytes_matched = flags & RE_FLAGS_BACKWARDS ? (int) input_backwards_size
|
||||
: (int) input_forwards_size;
|
||||
const uint8_t* ip = code;
|
||||
|
||||
input_incr = flags & RE_FLAGS_BACKWARDS ? -1 : 1;
|
||||
// Create the first position in the list, which points to the start of the
|
||||
// input data. Intially this is the only position, more positions will be
|
||||
// created every time RE_OPCODE_REPEAT_ANY_UNGREEDY is executed.
|
||||
FAIL_ON_ERROR(_yr_re_fast_exec_position_create(
|
||||
&context->re_fast_exec_position_pool, &first));
|
||||
|
||||
first->round = 0;
|
||||
first->input = input_data;
|
||||
first->prev = NULL;
|
||||
first->next = NULL;
|
||||
|
||||
if (flags & RE_FLAGS_BACKWARDS)
|
||||
input--;
|
||||
first->input--;
|
||||
|
||||
code_stack[sp] = code;
|
||||
input_stack[sp] = input;
|
||||
matches_stack[sp] = 0;
|
||||
sp++;
|
||||
// As we are starting with a single position, the last one and the first one
|
||||
// are the same.
|
||||
last = first;
|
||||
|
||||
while (sp > 0)
|
||||
// Round is incremented with every regxp instruction.
|
||||
int round = 0;
|
||||
|
||||
// Keep in the loop until the list of positions gets empty. Positions are
|
||||
// removed from the list when they don't match the current instruction in
|
||||
// the code.
|
||||
while (first != NULL)
|
||||
{
|
||||
sp--;
|
||||
ip = code_stack[sp];
|
||||
input = input_stack[sp];
|
||||
bytes_matched = matches_stack[sp];
|
||||
stop = false;
|
||||
RE_FAST_EXEC_POSITION* current = first;
|
||||
|
||||
while (!stop)
|
||||
// Iterate over all the positions, executing the current instruction at
|
||||
// all of them.
|
||||
while (current != NULL)
|
||||
{
|
||||
if (*ip == RE_OPCODE_MATCH)
|
||||
RE_FAST_EXEC_POSITION* next = current->next;
|
||||
|
||||
// Ignore any position in the list whose round number is different from
|
||||
// the current round. This prevents new positions added to the list in
|
||||
// this round from being taken into account in this same round.
|
||||
if (current->round != round)
|
||||
{
|
||||
current = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
bytes_matched = flags & RE_FLAGS_BACKWARDS
|
||||
? input_data - current->input - 1
|
||||
: current->input - input_data;
|
||||
uint8_t mask;
|
||||
uint8_t value;
|
||||
|
||||
bool match = false;
|
||||
|
||||
switch (*ip)
|
||||
{
|
||||
case RE_OPCODE_ANY:
|
||||
if (bytes_matched >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
match = true;
|
||||
current->input += input_incr;
|
||||
break;
|
||||
|
||||
case RE_OPCODE_LITERAL:
|
||||
if (bytes_matched >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
if (*current->input == *(ip + 1))
|
||||
{
|
||||
match = true;
|
||||
current->input += input_incr;
|
||||
}
|
||||
break;
|
||||
|
||||
case RE_OPCODE_MASKED_LITERAL:
|
||||
if (bytes_matched >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
value = *(int16_t*) (ip + 1) & 0xFF;
|
||||
mask = *(int16_t*) (ip + 1) >> 8;
|
||||
|
||||
if ((*current->input & mask) == value)
|
||||
{
|
||||
match = true;
|
||||
current->input += input_incr;
|
||||
}
|
||||
break;
|
||||
|
||||
case RE_OPCODE_REPEAT_ANY_UNGREEDY:
|
||||
if (bytes_matched >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
repeat_any_args = (RE_REPEAT_ANY_ARGS*) (ip + 1);
|
||||
match = true;
|
||||
|
||||
const uint8_t* next_opcode = ip + 1 + sizeof(RE_REPEAT_ANY_ARGS);
|
||||
|
||||
// insertion_point indicates the item in the list of inputs after which
|
||||
// the newly created inputs will be inserted.
|
||||
RE_FAST_EXEC_POSITION* insertion_point = current;
|
||||
|
||||
for (int j = repeat_any_args->min + 1; j <= repeat_any_args->max; j++)
|
||||
{
|
||||
// bytes_matched is the number of bytes matched so far, and j is the
|
||||
// minimum number of bytes that are still pending to match. If these
|
||||
// two numbers sum more than the maximum number of bytes that can be
|
||||
// matched the loop can be aborted. The position that we were about
|
||||
// to create can't lead to a match without exceeding
|
||||
// max_bytes_matched.
|
||||
if (bytes_matched + j >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
const uint8_t* next_input = current->input + j * input_incr;
|
||||
|
||||
// Find the point where next_input should be inserted. The list is
|
||||
// kept sorted by pointer in increasing order, the insertion point is
|
||||
// an item that has a pointer lower or equal than next_input, but
|
||||
// whose next item have a pointer that is larger.
|
||||
while (insertion_point->next != NULL &&
|
||||
next_input >= insertion_point->next->input)
|
||||
{
|
||||
insertion_point = insertion_point->next;
|
||||
}
|
||||
|
||||
// If the pointer at the insertion point is equal to next_input we
|
||||
// don't need to insert next_input in the list as this input already
|
||||
// exists.
|
||||
if (next_input == insertion_point->input)
|
||||
continue;
|
||||
|
||||
// The next opcode is RE_OPCODE_LITERAL, but the literal doesn't
|
||||
// match with the byte at next_input, we don't need to add this
|
||||
// input to the list as we already know that it won't match in the
|
||||
// next round and will be deleted from the list anyways.
|
||||
if (*(next_opcode) == RE_OPCODE_LITERAL &&
|
||||
*(next_opcode + 1) != *next_input)
|
||||
continue;
|
||||
|
||||
RE_FAST_EXEC_POSITION* new_input;
|
||||
|
||||
FAIL_ON_ERROR_WITH_CLEANUP(
|
||||
_yr_re_fast_exec_position_create(
|
||||
&context->re_fast_exec_position_pool, &new_input),
|
||||
// Cleanup
|
||||
_yr_re_fast_exec_destroy_position_list(
|
||||
&context->re_fast_exec_position_pool, first, last));
|
||||
|
||||
new_input->input = next_input;
|
||||
new_input->round = round + 1;
|
||||
new_input->prev = insertion_point;
|
||||
new_input->next = insertion_point->next;
|
||||
insertion_point->next = new_input;
|
||||
|
||||
if (new_input->next != NULL)
|
||||
new_input->next->prev = new_input;
|
||||
|
||||
if (insertion_point == last)
|
||||
last = new_input;
|
||||
}
|
||||
|
||||
current->input += input_incr * repeat_any_args->min;
|
||||
break;
|
||||
|
||||
case RE_OPCODE_MATCH:
|
||||
|
||||
if (flags & RE_FLAGS_EXHAUSTIVE)
|
||||
{
|
||||
FAIL_ON_ERROR(callback(
|
||||
// When matching forwards the matching data always starts at
|
||||
// input_data, when matching backwards it starts at input + 1 or
|
||||
// input_data - input_backwards_size if input + 1 is outside the
|
||||
// buffer.
|
||||
flags & RE_FLAGS_BACKWARDS
|
||||
? yr_max(input + 1, input_data - input_backwards_size)
|
||||
: input_data,
|
||||
// The number of matched bytes should not be larger than
|
||||
// max_bytes_matched.
|
||||
yr_min(bytes_matched, max_bytes_matched),
|
||||
flags,
|
||||
callback_args));
|
||||
|
||||
break;
|
||||
FAIL_ON_ERROR_WITH_CLEANUP(
|
||||
callback(
|
||||
// When matching forwards the matching data always starts at
|
||||
// input_data, when matching backwards it starts at input + 1
|
||||
// or input_data - input_backwards_size if input + 1 is
|
||||
// outside the buffer.
|
||||
flags & RE_FLAGS_BACKWARDS
|
||||
? yr_max(
|
||||
current->input + 1,
|
||||
input_data - input_backwards_size)
|
||||
: input_data,
|
||||
// The number of matched bytes should not be larger than
|
||||
// max_bytes_matched.
|
||||
yr_min(bytes_matched, max_bytes_matched),
|
||||
flags,
|
||||
callback_args),
|
||||
// Cleanup
|
||||
_yr_re_fast_exec_destroy_position_list(
|
||||
&context->re_fast_exec_position_pool, first, last));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (matches != NULL)
|
||||
*matches = bytes_matched;
|
||||
|
||||
_yr_re_fast_exec_destroy_position_list(
|
||||
&context->re_fast_exec_position_pool, first, last);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_matched >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
switch (*ip)
|
||||
{
|
||||
case RE_OPCODE_LITERAL:
|
||||
|
||||
if (*input == *(ip + 1))
|
||||
{
|
||||
bytes_matched++;
|
||||
input += input_incr;
|
||||
ip += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RE_OPCODE_MASKED_LITERAL:
|
||||
|
||||
value = *(int16_t*) (ip + 1) & 0xFF;
|
||||
mask = *(int16_t*) (ip + 1) >> 8;
|
||||
|
||||
if ((*input & mask) == value)
|
||||
{
|
||||
bytes_matched++;
|
||||
input += input_incr;
|
||||
ip += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RE_OPCODE_ANY:
|
||||
|
||||
bytes_matched++;
|
||||
input += input_incr;
|
||||
ip += 1;
|
||||
|
||||
break;
|
||||
|
||||
case RE_OPCODE_REPEAT_ANY_UNGREEDY:
|
||||
|
||||
repeat_any_args = (RE_REPEAT_ANY_ARGS*) (ip + 1);
|
||||
next_opcode = ip + 1 + sizeof(RE_REPEAT_ANY_ARGS);
|
||||
|
||||
for (int i = repeat_any_args->min + 1; i <= repeat_any_args->max; i++)
|
||||
{
|
||||
if (bytes_matched + i >= max_bytes_matched)
|
||||
break;
|
||||
|
||||
next_input = input + i * input_incr;
|
||||
|
||||
if (*(next_opcode) != RE_OPCODE_LITERAL ||
|
||||
(*(next_opcode) == RE_OPCODE_LITERAL &&
|
||||
*(next_opcode + 1) == *next_input))
|
||||
{
|
||||
if (sp >= YR_MAX_FAST_RE_STACK)
|
||||
return ERROR_TOO_MANY_RE_FIBERS;
|
||||
|
||||
code_stack[sp] = next_opcode;
|
||||
input_stack[sp] = next_input;
|
||||
matches_stack[sp] = bytes_matched + i;
|
||||
sp++;
|
||||
}
|
||||
}
|
||||
|
||||
input += input_incr * repeat_any_args->min;
|
||||
bytes_matched += repeat_any_args->min;
|
||||
|
||||
ip = next_opcode;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("non-supported opcode: %d\n", *ip);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
current->round = round + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove current from the list. If the item being removed is the first
|
||||
// one or the last one, the first and last pointers should be updated.
|
||||
// The removed item is put into the pool for later reuse.
|
||||
|
||||
if (current == first)
|
||||
first = current->next;
|
||||
|
||||
if (current == last)
|
||||
last = current->prev;
|
||||
|
||||
if (current->prev != NULL)
|
||||
current->prev->next = current->next;
|
||||
|
||||
if (current->next != NULL)
|
||||
current->next->prev = current->prev;
|
||||
|
||||
current->prev = NULL;
|
||||
current->next = context->re_fast_exec_position_pool.head;
|
||||
|
||||
if (current->next != NULL)
|
||||
current->next->prev = current;
|
||||
|
||||
context->re_fast_exec_position_pool.head = current;
|
||||
}
|
||||
|
||||
current = next;
|
||||
|
||||
} // while (current != NULL)
|
||||
|
||||
// Move instruction pointer (ip) to next instruction. Each opcode has
|
||||
// a different size.
|
||||
switch (*ip)
|
||||
{
|
||||
case RE_OPCODE_ANY:
|
||||
ip += 1;
|
||||
break;
|
||||
case RE_OPCODE_LITERAL:
|
||||
ip += 2;
|
||||
break;
|
||||
case RE_OPCODE_MASKED_LITERAL:
|
||||
ip += 3;
|
||||
break;
|
||||
case RE_OPCODE_REPEAT_ANY_UNGREEDY:
|
||||
ip += 1 + sizeof(RE_REPEAT_ANY_ARGS);
|
||||
break;
|
||||
case RE_OPCODE_MATCH:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
round++;
|
||||
}
|
||||
|
||||
// If this point is reached no matches were found.
|
||||
|
||||
if (matches != NULL)
|
||||
*matches = -1;
|
||||
|
||||
|
||||
+1094
-1098
File diff suppressed because it is too large
Load Diff
+35
-34
@@ -31,10 +31,10 @@
|
||||
version 2.2 of Bison. */
|
||||
|
||||
#ifndef YY_RE_YY_RE_GRAMMAR_H_INCLUDED
|
||||
#define YY_RE_YY_RE_GRAMMAR_H_INCLUDED
|
||||
# define YY_RE_YY_RE_GRAMMAR_H_INCLUDED
|
||||
/* Debug traces. */
|
||||
#ifndef YYDEBUG
|
||||
#define YYDEBUG 0
|
||||
# define YYDEBUG 0
|
||||
#endif
|
||||
#if YYDEBUG
|
||||
extern int re_yydebug;
|
||||
@@ -42,43 +42,43 @@ extern int re_yydebug;
|
||||
|
||||
/* Token type. */
|
||||
#ifndef YYTOKENTYPE
|
||||
#define YYTOKENTYPE
|
||||
enum yytokentype
|
||||
{
|
||||
_CHAR_ = 258,
|
||||
_ANY_ = 259,
|
||||
_RANGE_ = 260,
|
||||
_CLASS_ = 261,
|
||||
_WORD_CHAR_ = 262,
|
||||
_NON_WORD_CHAR_ = 263,
|
||||
_SPACE_ = 264,
|
||||
_NON_SPACE_ = 265,
|
||||
_DIGIT_ = 266,
|
||||
_NON_DIGIT_ = 267,
|
||||
_WORD_BOUNDARY_ = 268,
|
||||
_NON_WORD_BOUNDARY_ = 269
|
||||
};
|
||||
# define YYTOKENTYPE
|
||||
enum yytokentype
|
||||
{
|
||||
_CHAR_ = 258,
|
||||
_ANY_ = 259,
|
||||
_RANGE_ = 260,
|
||||
_CLASS_ = 261,
|
||||
_WORD_CHAR_ = 262,
|
||||
_NON_WORD_CHAR_ = 263,
|
||||
_SPACE_ = 264,
|
||||
_NON_SPACE_ = 265,
|
||||
_DIGIT_ = 266,
|
||||
_NON_DIGIT_ = 267,
|
||||
_WORD_BOUNDARY_ = 268,
|
||||
_NON_WORD_BOUNDARY_ = 269
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
#define _CHAR_ 258
|
||||
#define _ANY_ 259
|
||||
#define _RANGE_ 260
|
||||
#define _CLASS_ 261
|
||||
#define _WORD_CHAR_ 262
|
||||
#define _NON_WORD_CHAR_ 263
|
||||
#define _SPACE_ 264
|
||||
#define _NON_SPACE_ 265
|
||||
#define _DIGIT_ 266
|
||||
#define _NON_DIGIT_ 267
|
||||
#define _WORD_BOUNDARY_ 268
|
||||
#define _CHAR_ 258
|
||||
#define _ANY_ 259
|
||||
#define _RANGE_ 260
|
||||
#define _CLASS_ 261
|
||||
#define _WORD_CHAR_ 262
|
||||
#define _NON_WORD_CHAR_ 263
|
||||
#define _SPACE_ 264
|
||||
#define _NON_SPACE_ 265
|
||||
#define _DIGIT_ 266
|
||||
#define _NON_DIGIT_ 267
|
||||
#define _WORD_BOUNDARY_ 268
|
||||
#define _NON_WORD_BOUNDARY_ 269
|
||||
|
||||
/* Value type. */
|
||||
#if !defined YYSTYPE && !defined YYSTYPE_IS_DECLARED
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 73 "re_grammar.y" /* yacc.c:1916 */
|
||||
#line 75 "re_grammar.y" /* yacc.c:1916 */
|
||||
|
||||
int integer;
|
||||
uint32_t range;
|
||||
@@ -89,11 +89,12 @@ union YYSTYPE
|
||||
};
|
||||
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
#define YYSTYPE_IS_TRIVIAL 1
|
||||
#define YYSTYPE_IS_DECLARED 1
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
|
||||
int re_yyparse(void* yyscanner, RE_LEX_ENVIRONMENT* lex_env);
|
||||
|
||||
int re_yyparse (void *yyscanner, RE_LEX_ENVIRONMENT *lex_env);
|
||||
|
||||
#endif /* !YY_RE_YY_RE_GRAMMAR_H_INCLUDED */
|
||||
|
||||
@@ -27,6 +27,8 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
|
||||
%{
|
||||
|
||||
#include <yara/integers.h>
|
||||
|
||||
+12
-6
@@ -279,16 +279,22 @@ YR_API void yr_scanner_destroy(YR_SCANNER* scanner)
|
||||
{
|
||||
YR_DEBUG_FPRINTF(2, stderr, "- %s() {} \n", __FUNCTION__);
|
||||
|
||||
RE_FIBER* fiber;
|
||||
RE_FIBER* next_fiber;
|
||||
|
||||
fiber = scanner->re_fiber_pool.fibers.head;
|
||||
RE_FIBER* fiber = scanner->re_fiber_pool.fibers.head;
|
||||
|
||||
while (fiber != NULL)
|
||||
{
|
||||
next_fiber = fiber->next;
|
||||
RE_FIBER* next = fiber->next;
|
||||
yr_free(fiber);
|
||||
fiber = next_fiber;
|
||||
fiber = next;
|
||||
}
|
||||
|
||||
RE_FAST_EXEC_POSITION* position = scanner->re_fast_exec_position_pool.head;
|
||||
|
||||
while (position != NULL)
|
||||
{
|
||||
RE_FAST_EXEC_POSITION* next = position->next;
|
||||
yr_free(position);
|
||||
position = next;
|
||||
}
|
||||
|
||||
if (scanner->objects_table != NULL)
|
||||
|
||||
@@ -1299,6 +1299,36 @@ static void test_hex_strings()
|
||||
condition: !a == 2 }",
|
||||
"122222222" TEXT_1024_BYTES);
|
||||
|
||||
assert_true_rule(
|
||||
"rule test { \
|
||||
strings: $a = { 30 31 32 [0-5] 38 39 } \
|
||||
condition: $a }",
|
||||
"0123456789");
|
||||
|
||||
assert_true_rule(
|
||||
"rule test { \
|
||||
strings: $a = { 31 32 [0-5] 38 39 30 } \
|
||||
condition: $a }",
|
||||
"1234567890");
|
||||
|
||||
assert_true_rule(
|
||||
"rule test { \
|
||||
strings: $a = { 31 32 [0-2] 34 [0-2] 34 } \
|
||||
condition: $a }",
|
||||
"1244");
|
||||
|
||||
assert_true_rule(
|
||||
"rule test { \
|
||||
strings: $a = { 31 32 [0-2] 34 [0-2] 34 } \
|
||||
condition: $a }",
|
||||
"12344");
|
||||
|
||||
assert_true_rule(
|
||||
"rule test { \
|
||||
strings: $a = { 31 32 [0-2] 34 [0-2] 34 [2-3] 34 } \
|
||||
condition: $a }",
|
||||
"123440004");
|
||||
|
||||
assert_error(
|
||||
"rule test { \
|
||||
strings: $a = { 01 [0] 02 } \
|
||||
|
||||
Reference in New Issue
Block a user