Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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