Skip to content

Commit

Permalink
Fix fuzzing errors
Browse files Browse the repository at this point in the history
- Fix invalid write in flecs_path_append
- Correctly handle name lookups with multiple consecutive separators
- Correctly handle attempts to create entities with ids > UINT32_MAX
- Improve cycle detection in observer code/prevent stack overflows
  • Loading branch information
SanderMertens committed Dec 14, 2024
1 parent 5d6b849 commit fd609fe
Show file tree
Hide file tree
Showing 12 changed files with 553 additions and 29 deletions.
68 changes: 55 additions & 13 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -7377,7 +7377,12 @@ int flecs_traverse_add(

/* If a name is provided but not yet assigned, add the Name component */
if (name && !name_assigned) {
ecs_add_path_w_sep(world, result, scope, name, sep, root_sep);
if (!ecs_add_path_w_sep(world, result, scope, name, sep, root_sep)) {
if (name[0] == '#') {
/* Numerical ids should always return, unless it's invalid */
goto error;
}
}
} else if (new_entity && scope) {
ecs_add_pair(world, result, EcsChildOf, scope);
}
Expand Down Expand Up @@ -11104,7 +11109,11 @@ ecs_entity_t flecs_name_to_id(
{
ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(name[0] == '#', ECS_INVALID_PARAMETER, NULL);
return flecs_ito(uint64_t, atoll(name + 1));
ecs_entity_t res = flecs_ito(uint64_t, atoll(name + 1));
if (res >= UINT32_MAX) {
return 0; /* Invalid id */
}
return res;
}

static
Expand Down Expand Up @@ -11178,7 +11187,7 @@ const char* flecs_path_elem(
}

if (buffer) {
if (pos == (size - 1)) {
if (pos >= (size - 1)) {
if (size == ECS_NAME_BUFFER_LENGTH) { /* stack buffer */
char *new_buffer = ecs_os_malloc(size * 2 + 1);
ecs_os_memcpy(new_buffer, buffer, size);
Expand All @@ -11201,7 +11210,7 @@ const char* flecs_path_elem(
*size_out = size;
}

if (pos) {
if (pos || ptr[0]) {
return ptr;
} else {
return NULL;
Expand Down Expand Up @@ -11229,8 +11238,11 @@ ecs_entity_t flecs_get_parent_from_path(
const char **path_ptr,
const char *sep,
const char *prefix,
bool new_entity)
bool new_entity,
bool *error)
{
ecs_assert(error != NULL, ECS_INTERNAL_ERROR, NULL);

bool start_from_root = false;
const char *path = *path_ptr;

Expand All @@ -11242,6 +11254,10 @@ ecs_entity_t flecs_get_parent_from_path(

if (path[0] == '#') {
parent = flecs_name_to_id(path);
if (!parent && ecs_os_strncmp(path, "#0", 2)) {
*error = true;
return 0;
}

path ++;
while (path[0] && isdigit(path[0])) {
Expand Down Expand Up @@ -11451,8 +11467,12 @@ ecs_entity_t ecs_lookup_path_w_sep(
sep = ".";
}

bool error = false;
parent = flecs_get_parent_from_path(
stage, parent, &path, sep, prefix, true);
stage, parent, &path, sep, prefix, true, &error);
if (error) {
return 0;
}

if (parent && !(parent = ecs_get_alive(world, parent))) {
return 0;
Expand Down Expand Up @@ -11630,8 +11650,13 @@ ecs_entity_t ecs_add_path_w_sep(
}

bool root_path = flecs_is_root_path(path, prefix);
bool error = false;
parent = flecs_get_parent_from_path(
world, parent, &path, sep, prefix, !entity);
world, parent, &path, sep, prefix, !entity, &error);
if (error) {
/* Invalid id */
return 0;
}

char buff[ECS_NAME_BUFFER_LENGTH];
const char *ptr = path;
Expand Down Expand Up @@ -13816,7 +13841,8 @@ void flecs_emit_forward_up(
ecs_table_t *table,
ecs_id_record_t *idr,
ecs_vec_t *stack,
ecs_vec_t *reachable_ids);
ecs_vec_t *reachable_ids,
int32_t depth);

static
void flecs_emit_forward_id(
Expand Down Expand Up @@ -14055,7 +14081,8 @@ void flecs_emit_forward_table_up(
ecs_record_t *tgt_record,
ecs_id_record_t *tgt_idr,
ecs_vec_t *stack,
ecs_vec_t *reachable_ids)
ecs_vec_t *reachable_ids,
int32_t depth)
{
ecs_allocator_t *a = &world->allocator;
int32_t i, id_count = tgt_table->type.count;
Expand Down Expand Up @@ -14092,6 +14119,13 @@ void flecs_emit_forward_table_up(
continue;
}

if (idr == tgt_idr) {
char *idstr = ecs_id_str(world, idr->id);
ecs_assert(idr != tgt_idr, ECS_CYCLE_DETECTED, idstr);
ecs_os_free(idstr);
return;
}

/* Id has the same relationship, traverse to find ids for forwarding */
if (ECS_PAIR_FIRST(id) == trav || ECS_PAIR_FIRST(id) == EcsIsA) {
ecs_table_t **t = ecs_vec_append_t(&world->allocator, stack,
Expand All @@ -14116,7 +14150,7 @@ void flecs_emit_forward_table_up(
/* Cache is dirty, traverse upwards */
do {
flecs_emit_forward_up(world, er, er_onset, emit_ids, it,
table, idr, stack, reachable_ids);
table, idr, stack, reachable_ids, depth);
if (++i >= id_count) {
break;
}
Expand Down Expand Up @@ -14198,8 +14232,16 @@ void flecs_emit_forward_up(
ecs_table_t *table,
ecs_id_record_t *idr,
ecs_vec_t *stack,
ecs_vec_t *reachable_ids)
ecs_vec_t *reachable_ids,
int32_t depth)
{
if (depth >= FLECS_DAG_DEPTH_MAX) {
char *idstr = ecs_id_str(world, idr->id);
ecs_assert(depth < FLECS_DAG_DEPTH_MAX, ECS_CYCLE_DETECTED, idstr);
ecs_os_free(idstr);
return;
}

ecs_id_t id = idr->id;
ecs_entity_t tgt = ECS_PAIR_SECOND(id);
tgt = flecs_entities_get_alive(world, tgt);
Expand All @@ -14211,7 +14253,7 @@ void flecs_emit_forward_up(
}

flecs_emit_forward_table_up(world, er, er_onset, emit_ids, it, table,
tgt, tgt_table, tgt_record, idr, stack, reachable_ids);
tgt, tgt_table, tgt_record, idr, stack, reachable_ids, depth + 1);
}

static
Expand Down Expand Up @@ -14239,7 +14281,7 @@ void flecs_emit_forward(
ecs_vec_init_t(&world->allocator, &stack, ecs_table_t*, 0);
ecs_vec_reset_t(&world->allocator, &rc->ids, ecs_reachable_elem_t);
flecs_emit_forward_up(world, er, er_onset, emit_ids, it, table,
idr, &stack, &rc->ids);
idr, &stack, &rc->ids, 0);
it->sources[0] = 0;
ecs_vec_fini_t(&world->allocator, &stack, ecs_table_t*);

Expand Down
8 changes: 8 additions & 0 deletions distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,14 @@
#define FLECS_QUERY_SCOPE_NESTING_MAX (8)
#endif

/** @def FLECS_DAG_DEPTH_MAX
* Maximum of levels in a DAG (acyclic relationship graph). If a graph with a
* depth larger than this is encountered, a CYCLE_DETECTED panic is thrown.
*/
#ifndef FLECS_DAG_DEPTH_MAX
#define FLECS_DAG_DEPTH_MAX (128)
#endif

/** @} */

/**
Expand Down
8 changes: 8 additions & 0 deletions include/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,14 @@
#define FLECS_QUERY_SCOPE_NESTING_MAX (8)
#endif

/** @def FLECS_DAG_DEPTH_MAX
* Maximum of levels in a DAG (acyclic relationship graph). If a graph with a
* depth larger than this is encountered, a CYCLE_DETECTED panic is thrown.
*/
#ifndef FLECS_DAG_DEPTH_MAX
#define FLECS_DAG_DEPTH_MAX (128)
#endif

/** @} */

#include "flecs/private/api_defines.h"
Expand Down
7 changes: 6 additions & 1 deletion src/entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,12 @@ int flecs_traverse_add(

/* If a name is provided but not yet assigned, add the Name component */
if (name && !name_assigned) {
ecs_add_path_w_sep(world, result, scope, name, sep, root_sep);
if (!ecs_add_path_w_sep(world, result, scope, name, sep, root_sep)) {
if (name[0] == '#') {
/* Numerical ids should always return, unless it's invalid */
goto error;
}
}
} else if (new_entity && scope) {
ecs_add_pair(world, result, EcsChildOf, scope);
}
Expand Down
32 changes: 26 additions & 6 deletions src/entity_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ ecs_entity_t flecs_name_to_id(
{
ecs_assert(name != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_assert(name[0] == '#', ECS_INVALID_PARAMETER, NULL);
return flecs_ito(uint64_t, atoll(name + 1));
ecs_entity_t res = flecs_ito(uint64_t, atoll(name + 1));
if (res >= UINT32_MAX) {
return 0; /* Invalid id */
}
return res;
}

static
Expand Down Expand Up @@ -205,7 +209,7 @@ const char* flecs_path_elem(
}

if (buffer) {
if (pos == (size - 1)) {
if (pos >= (size - 1)) {
if (size == ECS_NAME_BUFFER_LENGTH) { /* stack buffer */
char *new_buffer = ecs_os_malloc(size * 2 + 1);
ecs_os_memcpy(new_buffer, buffer, size);
Expand All @@ -228,7 +232,7 @@ const char* flecs_path_elem(
*size_out = size;
}

if (pos) {
if (pos || ptr[0]) {
return ptr;
} else {
return NULL;
Expand Down Expand Up @@ -256,8 +260,11 @@ ecs_entity_t flecs_get_parent_from_path(
const char **path_ptr,
const char *sep,
const char *prefix,
bool new_entity)
bool new_entity,
bool *error)
{
ecs_assert(error != NULL, ECS_INTERNAL_ERROR, NULL);

bool start_from_root = false;
const char *path = *path_ptr;

Expand All @@ -269,6 +276,10 @@ ecs_entity_t flecs_get_parent_from_path(

if (path[0] == '#') {
parent = flecs_name_to_id(path);
if (!parent && ecs_os_strncmp(path, "#0", 2)) {
*error = true;
return 0;
}

path ++;
while (path[0] && isdigit(path[0])) {
Expand Down Expand Up @@ -478,8 +489,12 @@ ecs_entity_t ecs_lookup_path_w_sep(
sep = ".";
}

bool error = false;
parent = flecs_get_parent_from_path(
stage, parent, &path, sep, prefix, true);
stage, parent, &path, sep, prefix, true, &error);
if (error) {
return 0;
}

if (parent && !(parent = ecs_get_alive(world, parent))) {
return 0;
Expand Down Expand Up @@ -657,8 +672,13 @@ ecs_entity_t ecs_add_path_w_sep(
}

bool root_path = flecs_is_root_path(path, prefix);
bool error = false;
parent = flecs_get_parent_from_path(
world, parent, &path, sep, prefix, !entity);
world, parent, &path, sep, prefix, !entity, &error);
if (error) {
/* Invalid id */
return 0;
}

char buff[ECS_NAME_BUFFER_LENGTH];
const char *ptr = path;
Expand Down
29 changes: 23 additions & 6 deletions src/observable.c
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ void flecs_emit_forward_up(
ecs_table_t *table,
ecs_id_record_t *idr,
ecs_vec_t *stack,
ecs_vec_t *reachable_ids);
ecs_vec_t *reachable_ids,
int32_t depth);

static
void flecs_emit_forward_id(
Expand Down Expand Up @@ -815,7 +816,8 @@ void flecs_emit_forward_table_up(
ecs_record_t *tgt_record,
ecs_id_record_t *tgt_idr,
ecs_vec_t *stack,
ecs_vec_t *reachable_ids)
ecs_vec_t *reachable_ids,
int32_t depth)
{
ecs_allocator_t *a = &world->allocator;
int32_t i, id_count = tgt_table->type.count;
Expand Down Expand Up @@ -852,6 +854,13 @@ void flecs_emit_forward_table_up(
continue;
}

if (idr == tgt_idr) {
char *idstr = ecs_id_str(world, idr->id);
ecs_assert(idr != tgt_idr, ECS_CYCLE_DETECTED, idstr);
ecs_os_free(idstr);
return;
}

/* Id has the same relationship, traverse to find ids for forwarding */
if (ECS_PAIR_FIRST(id) == trav || ECS_PAIR_FIRST(id) == EcsIsA) {
ecs_table_t **t = ecs_vec_append_t(&world->allocator, stack,
Expand All @@ -876,7 +885,7 @@ void flecs_emit_forward_table_up(
/* Cache is dirty, traverse upwards */
do {
flecs_emit_forward_up(world, er, er_onset, emit_ids, it,
table, idr, stack, reachable_ids);
table, idr, stack, reachable_ids, depth);
if (++i >= id_count) {
break;
}
Expand Down Expand Up @@ -958,8 +967,16 @@ void flecs_emit_forward_up(
ecs_table_t *table,
ecs_id_record_t *idr,
ecs_vec_t *stack,
ecs_vec_t *reachable_ids)
ecs_vec_t *reachable_ids,
int32_t depth)
{
if (depth >= FLECS_DAG_DEPTH_MAX) {
char *idstr = ecs_id_str(world, idr->id);
ecs_assert(depth < FLECS_DAG_DEPTH_MAX, ECS_CYCLE_DETECTED, idstr);
ecs_os_free(idstr);
return;
}

ecs_id_t id = idr->id;
ecs_entity_t tgt = ECS_PAIR_SECOND(id);
tgt = flecs_entities_get_alive(world, tgt);
Expand All @@ -971,7 +988,7 @@ void flecs_emit_forward_up(
}

flecs_emit_forward_table_up(world, er, er_onset, emit_ids, it, table,
tgt, tgt_table, tgt_record, idr, stack, reachable_ids);
tgt, tgt_table, tgt_record, idr, stack, reachable_ids, depth + 1);
}

static
Expand Down Expand Up @@ -999,7 +1016,7 @@ void flecs_emit_forward(
ecs_vec_init_t(&world->allocator, &stack, ecs_table_t*, 0);
ecs_vec_reset_t(&world->allocator, &rc->ids, ecs_reachable_elem_t);
flecs_emit_forward_up(world, er, er_onset, emit_ids, it, table,
idr, &stack, &rc->ids);
idr, &stack, &rc->ids, 0);
it->sources[0] = 0;
ecs_vec_fini_t(&world->allocator, &stack, ecs_table_t*);

Expand Down
5 changes: 4 additions & 1 deletion test/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@
"entity_w_parent_w_add",
"entity_w_parent_w_add_w_parent",
"entity_w_parent_w_set",
"entity_w_parent_w_set_w_parent"
"entity_w_parent_w_set_w_parent",
"entity_w_new_id_and_double_dot",
"entity_w_existing_id_and_double_dot",
"entity_w_large_id_name"
]
}, {
"id": "Each",
Expand Down
Loading

0 comments on commit fd609fe

Please sign in to comment.