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:
Victor M. Alvarez
2021-07-08 09:47:47 +02:00
committed by GitHub
parent c937f4770a
commit 48cc15b447
10 changed files with 1597 additions and 1355 deletions
-3
View File
@@ -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
-6
View File
@@ -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 }.
+19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+35 -34
View File
@@ -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 */
+2
View File
@@ -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
View File
@@ -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)
+30
View File
@@ -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 } \