fix: validate arena buffer count

Ensure `YR_ARENA` objects are created, loaded, and accessed with a valid number of buffers. This prevents out-of-bounds memory access and improves robustness against corrupted arena data or incorrect API usage.

Closes #2238.
This commit is contained in:
Victor M. Alvarez
2026-08-25 10:29:10 +02:00
parent dcc4b9166c
commit 0cdff36723
4 changed files with 108 additions and 4 deletions
+6 -3
View File
@@ -137,7 +137,7 @@ static int _yr_arena_allocate_memory(
size_t size,
YR_ARENA_REF* ref)
{
if (buffer_id > arena->num_buffers)
if (buffer_id >= arena->num_buffers)
return ERROR_INVALID_ARGUMENT;
YR_ARENA_BUFFER* b = &arena->buffers[buffer_id];
@@ -234,6 +234,9 @@ int yr_arena_create(
size_t initial_buffer_size,
YR_ARENA** arena)
{
if (num_buffers == 0 || num_buffers > YR_MAX_ARENA_BUFFERS)
return ERROR_INVALID_ARGUMENT;
YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA));
if (new_arena == NULL)
@@ -555,8 +558,8 @@ int yr_arena_load_stream(YR_STREAM* stream, YR_ARENA** arena)
if (hdr.version != YR_ARENA_FILE_VERSION)
return ERROR_UNSUPPORTED_FILE_VERSION;
if (hdr.num_buffers > YR_MAX_ARENA_BUFFERS)
return ERROR_INVALID_FILE;
if (hdr.num_buffers == 0 || hdr.num_buffers > YR_MAX_ARENA_BUFFERS)
return ERROR_CORRUPT_FILE;
YR_ARENA_FILE_BUFFER buffers[YR_MAX_ARENA_BUFFERS];
+3
View File
@@ -325,6 +325,9 @@ YR_API int yr_rules_scan_proc(
int yr_rules_from_arena(YR_ARENA* arena, YR_RULES** rules)
{
if (arena->num_buffers < YR_NUM_SECTIONS)
return ERROR_CORRUPT_FILE;
YR_SUMMARY* summary = (YR_SUMMARY*) yr_arena_get_ptr(
arena, YR_SUMMARY_SECTION, 0);
+50
View File
@@ -886,6 +886,55 @@ void test_load_rules_corrupt_ac_transition()
yr_finalize();
}
void test_load_rules_corrupt_num_buffers()
{
yr_initialize();
// Test num_buffers = 0
uint8_t data0[] = {'Y', 'A', 'R', 'A', YR_ARENA_FILE_VERSION, 0, 4, 0};
FILE* fh = fopen("test-corrupt-num0.yarc", "wb");
assert_true_expr(fh != NULL);
assert_true_expr(fwrite(data0, 1, sizeof(data0), fh) == sizeof(data0));
fclose(fh);
YR_RULES* rules = NULL;
int result = yr_rules_load("test-corrupt-num0.yarc", &rules);
if (result != ERROR_CORRUPT_FILE)
{
fprintf(
stderr,
"test_load_rules_corrupt_num_buffers (num_buffers=0): expecting "
"ERROR_CORRUPT_FILE, got %d\n",
result);
exit(EXIT_FAILURE);
}
// Test num_buffers = 1 (fewer than YR_NUM_SECTIONS)
uint8_t data1[6 + 12];
memset(data1, 0, sizeof(data1));
memcpy(data1, "YARA", 4);
data1[4] = YR_ARENA_FILE_VERSION;
data1[5] = 1;
fh = fopen("test-corrupt-num1.yarc", "wb");
assert_true_expr(fh != NULL);
assert_true_expr(fwrite(data1, 1, sizeof(data1), fh) == sizeof(data1));
fclose(fh);
result = yr_rules_load("test-corrupt-num1.yarc", &rules);
if (result != ERROR_CORRUPT_FILE)
{
fprintf(
stderr,
"test_load_rules_corrupt_num_buffers (num_buffers=1): expecting "
"ERROR_CORRUPT_FILE, got %d\n",
result);
exit(EXIT_FAILURE);
}
yr_finalize();
}
void test_scanner()
{
const char* buf = "dummy";
@@ -1521,6 +1570,7 @@ int main(int argc, char** argv)
test_load_rules_corrupt_summary();
test_load_rules_bad_external_type();
test_load_rules_corrupt_ac_transition();
test_load_rules_corrupt_num_buffers();
test_scanner();
test_xor_key_string_in_atom();
test_ast_callback();
+49 -1
View File
@@ -39,11 +39,19 @@ static void basic_tests()
yr_initialize();
// Create arena with 1 buffers of 10 bytes of initial size
assert_true_expr(yr_arena_create(0, 10, &arena) == ERROR_INVALID_ARGUMENT);
assert_true_expr(
yr_arena_create(YR_MAX_ARENA_BUFFERS + 1, 10, &arena) ==
ERROR_INVALID_ARGUMENT);
// Create arena with 2 buffers of 10 bytes of initial size
assert_true_expr(yr_arena_create(2, 10, &arena) == ERROR_SUCCESS);
YR_ARENA_REF ref;
assert_true_expr(
yr_arena_allocate_memory(arena, 2, 5, &ref) == ERROR_INVALID_ARGUMENT);
// Allocate 5 bytes.
assert_true_expr(
yr_arena_allocate_memory(arena, 0, 5, &ref) == ERROR_SUCCESS);
@@ -330,6 +338,45 @@ static void corrupt_reloc_target_tests()
yr_finalize();
}
static void corrupt_num_buffers_tests()
{
yr_initialize();
// Test loading with num_buffers = 0
uint8_t data[6] = {'Y', 'A', 'R', 'A', YR_ARENA_FILE_VERSION, 0};
FILE* fh = fopen("test-arena-corrupt-num0", "w+b");
assert_true_expr(fh != NULL);
fwrite(data, 1, sizeof(data), fh);
fflush(fh);
fseek(fh, 0, SEEK_SET);
YR_STREAM stream;
stream.user_data = fh;
stream.read = (YR_STREAM_READ_FUNC) fread;
stream.write = (YR_STREAM_WRITE_FUNC) fwrite;
YR_ARENA* arena = NULL;
assert_true_expr(
yr_arena_load_stream(&stream, &arena) == ERROR_CORRUPT_FILE);
fclose(fh);
// Test loading with num_buffers > YR_MAX_ARENA_BUFFERS
data[5] = YR_MAX_ARENA_BUFFERS + 1;
fh = fopen("test-arena-corrupt-num-max", "w+b");
assert_true_expr(fh != NULL);
fwrite(data, 1, sizeof(data), fh);
fflush(fh);
fseek(fh, 0, SEEK_SET);
stream.user_data = fh;
assert_true_expr(
yr_arena_load_stream(&stream, &arena) == ERROR_CORRUPT_FILE);
fclose(fh);
yr_finalize();
}
int main(int argc, char** argv)
{
int result = 0;
@@ -342,6 +389,7 @@ int main(int argc, char** argv)
corrupt_stream_tests();
corrupt_reloc_offset_tests();
corrupt_reloc_target_tests();
corrupt_num_buffers_tests();
YR_DEBUG_FPRINTF(
1, stderr, "} = %d // %s() in %s\n", result, __FUNCTION__, argv[0]);