Skip to content

Commit

Permalink
a new too slow scanning callback
Browse files Browse the repository at this point in the history
  • Loading branch information
regeciovad authored and regeciova committed May 23, 2023
1 parent c506276 commit f759ca9
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 1 deletion.
22 changes: 22 additions & 0 deletions cli/yara.c
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,28 @@ static int callback(

return CALLBACK_CONTINUE;

case CALLBACK_MSG_TOO_SLOW_SCANNING:
if (ignore_warnings)
return CALLBACK_CONTINUE;

string = (YR_STRING*) message_data;
rule = &context->rules->rules_table[string->rule_idx];

if (rule != NULL && string != NULL)
fprintf(
stderr,
"warning: rule \"%s\": scanning with string %s is taking a very long "
"time, it is either too general or very common.\n",
rule->identifier,
string->identifier);
else
return CALLBACK_CONTINUE;

if (fail_on_warnings)
return CALLBACK_ERROR;

return CALLBACK_CONTINUE;

case CALLBACK_MSG_TOO_MANY_MATCHES:
if (ignore_warnings)
return CALLBACK_CONTINUE;
Expand Down
1 change: 1 addition & 0 deletions libyara/include/yara/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define ERROR_INVALID_PERCENTAGE 62
#define ERROR_IDENTIFIER_MATCHES_WILDCARD 63
#define ERROR_INVALID_VALUE 64
#define ERROR_TOO_SLOW_SCANNING 65

#define GOTO_EXIT_ON_ERROR(x) \
{ \
Expand Down
12 changes: 12 additions & 0 deletions libyara/include/yara/limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define YR_MAX_STRING_MATCHES 1000000
#endif

// The number of matches before detecting slow scanning. If more matches are found
// the scan will have a CALLBACK_MSG_TOO_SLOW_SCANNING.
#ifndef YR_SLOW_STRING_MATCHES
#define YR_SLOW_STRING_MATCHES 600000
#endif

// If size of the input is bigger then 0.2 MB and 0-length atoms are used
// the scan will have a CALLBACK_MSG_TOO_SLOW_SCANNING.
#ifndef YR_FILE_SIZE_THRESHOLD
#define YR_FILE_SIZE_THRESHOLD 200000
#endif

// Maximum number of argument that a function in a YARA module can have.
#ifndef YR_MAX_FUNCTION_ARGS
#define YR_MAX_FUNCTION_ARGS 128
Expand Down
1 change: 1 addition & 0 deletions libyara/include/yara/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CALLBACK_MSG_MODULE_IMPORTED 5
#define CALLBACK_MSG_TOO_MANY_MATCHES 6
#define CALLBACK_MSG_CONSOLE_LOG 7
#define CALLBACK_MSG_TOO_SLOW_SCANNING 8

#define CALLBACK_CONTINUE 0
#define CALLBACK_ABORT 1
Expand Down
45 changes: 44 additions & 1 deletion libyara/scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ static int _yr_scanner_scan_mem_block(
size_t i = 0;
uint32_t state = YR_AC_ROOT_STATE;
uint16_t index;
YR_STRING* report_string = NULL;
YR_RULE* rule = NULL;

while (i < block->size)
{
Expand Down Expand Up @@ -104,6 +106,14 @@ static int _yr_scanner_scan_mem_block(

match = &rules->ac_match_pool[match_table[state] - 1];

if (scanner->matches->count >= YR_SLOW_STRING_MATCHES)
{
report_string = match->string;
rule = report_string
? &scanner->rules->rules_table[report_string->rule_idx]
: NULL;
}

while (match != NULL)
{
if (match->backtrack <= i)
Expand Down Expand Up @@ -162,6 +172,25 @@ static int _yr_scanner_scan_mem_block(
}
}

if (rule != NULL &&
scanner->matches->count >= YR_SLOW_STRING_MATCHES &&
scanner->matches->count < YR_MAX_STRING_MATCHES)
{
if (rule != NULL && report_string != NULL)
{
result = scanner->callback(
scanner,
CALLBACK_MSG_TOO_SLOW_SCANNING,
(void*) report_string,
scanner->user_data);
if (result != CALLBACK_CONTINUE)
{
result = ERROR_TOO_SLOW_SCANNING;
goto _exit;
}
}
}

_exit:

YR_DEBUG_FPRINTF(
Expand Down Expand Up @@ -655,6 +684,7 @@ YR_API int yr_scanner_scan_mem(

YR_MEMORY_BLOCK block;
YR_MEMORY_BLOCK_ITERATOR iterator;
int result;

block.size = buffer_size;
block.base = 0;
Expand All @@ -667,7 +697,20 @@ YR_API int yr_scanner_scan_mem(
iterator.file_size = _yr_get_file_size;
iterator.last_error = ERROR_SUCCESS;

int result = yr_scanner_scan_mem_blocks(scanner, &iterator);
// Detect cases where every byte of input is checked for match and input size is bigger then 0.2 MB
if (scanner->rules->ac_match_table[YR_AC_ROOT_STATE] != 0 && buffer_size > YR_FILE_SIZE_THRESHOLD)
{
YR_STRING* report_string = scanner->rules->ac_match_pool[YR_AC_ROOT_STATE].string;
result = scanner->callback(
scanner,
CALLBACK_MSG_TOO_SLOW_SCANNING,
(void*) report_string,
scanner->user_data);
if (result != CALLBACK_CONTINUE)
return ERROR_TOO_SLOW_SCANNING;
}

result = yr_scanner_scan_mem_blocks(scanner, &iterator);

YR_DEBUG_FPRINTF(
2,
Expand Down
175 changes: 175 additions & 0 deletions tests/test-api.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,179 @@ void test_too_many_matches()
yr_finalize();
}

int ignore_too_slow_scanning(
YR_SCAN_CONTEXT* context,
int message,
void* message_data,
void* user_data)
{
return CALLBACK_CONTINUE;
}

int propagate_too_slow_scanning(
YR_SCAN_CONTEXT* context,
int message,
void* message_data,
void* user_data)
{
if (message == CALLBACK_MSG_TOO_SLOW_SCANNING)
return CALLBACK_ERROR;

return CALLBACK_CONTINUE;
}

void test_too_slow_scanning_slow_string_matches()
{
YR_RULES* rules;

char* rules_str = "\
rule t { \
strings: \
$a = /a.{0,18}/ \
condition: \
$a \
}";

yr_initialize();

if (compile_rule(rules_str, &rules) != ERROR_SUCCESS)
{
perror("compile_rule");
exit(EXIT_FAILURE);
}

uint8_t* buffer = (uint8_t*) malloc(YR_SLOW_STRING_MATCHES+100);

if (buffer == NULL)
{
perror("malloc");
exit(EXIT_FAILURE);
}

memset(buffer, 'a', YR_SLOW_STRING_MATCHES+100);

int err = yr_rules_scan_mem(
rules,
(const uint8_t*) buffer,
YR_SLOW_STRING_MATCHES+100,
0,
propagate_too_slow_scanning,
NULL,
0);

if (err != ERROR_TOO_SLOW_SCANNING)
{
fprintf(
stderr,
"test_too_slow_scanning_slow_string_matches failed, expecting ERROR_TOO_SLOW_SCANNING, got "
"%d\n",
err);

free(buffer);
exit(EXIT_FAILURE);
}

err = yr_rules_scan_mem(
rules,
(const uint8_t*) buffer,
YR_SLOW_STRING_MATCHES+100,
0,
ignore_too_slow_scanning,
NULL,
0);

if (err != ERROR_SUCCESS)
{
fprintf(
stderr,
"test_too_slow_scanning_slow_string_matches failed, expecting ERROR_SUCCESS, got %d\n",
err);

free(buffer);
exit(EXIT_FAILURE);
}

free(buffer);
yr_rules_destroy(rules);
yr_finalize();
}

void test_too_slow_scanning()
{
YR_RULES* rules;

char* rules_str = "\
rule t { \
strings: \
$a = /\\w+/ \
condition: \
$a \
}";

yr_initialize();

if (compile_rule(rules_str, &rules) != ERROR_SUCCESS)
{
perror("compile_rule");
exit(EXIT_FAILURE);
}

uint8_t* buffer = (uint8_t*) malloc(2 * YR_FILE_SIZE_THRESHOLD);

if (buffer == NULL)
{
perror("malloc");
exit(EXIT_FAILURE);
}

memset(buffer, 'a', 2 * YR_FILE_SIZE_THRESHOLD);

int err = yr_rules_scan_mem(
rules,
(const uint8_t*) buffer,
2 * YR_FILE_SIZE_THRESHOLD,
0,
propagate_too_slow_scanning,
NULL,
0);

if (err != ERROR_TOO_SLOW_SCANNING)
{
fprintf(
stderr,
"test_too_slow_scanning failed, expecting ERROR_TOO_SLOW_SCANNING, got "
"%d\n",
err);

free(buffer);
exit(EXIT_FAILURE);
}

err = yr_rules_scan_mem(
rules,
(const uint8_t*) buffer,
2 * CALLBACK_MSG_TOO_SLOW_SCANNING,
0,
ignore_too_slow_scanning,
NULL,
0);

if (err != ERROR_SUCCESS)
{
fprintf(
stderr,
"test_too_slow_scanning failed, expecting ERROR_SUCCESS, got %d\n",
err);

free(buffer);
exit(EXIT_FAILURE);
}

free(buffer);
yr_rules_destroy(rules);
yr_finalize();
}

void test_save_load_rules()
{
YR_COMPILER* compiler = NULL;
Expand Down Expand Up @@ -1099,6 +1272,8 @@ int main(int argc, char** argv)
test_max_string_per_rules();
test_max_match_data();
test_too_many_matches();
test_too_slow_scanning();
test_too_slow_scanning_slow_string_matches();
test_include_callback();
test_save_load_rules();
test_scanner();
Expand Down

0 comments on commit f759ca9

Please sign in to comment.