Added max recusrion depth for cJSONDuplicate to prevent stack exhaustion in case of circular reference

This commit is contained in:
Nicolas Badoux 2024-08-30 13:36:22 +02:00 committed by Alan Wang
parent 078c4e6c53
commit 9d1b229086
3 changed files with 35 additions and 1 deletions

12
cJSON.c
View File

@ -2737,7 +2737,14 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int co
} }
/* Duplication */ /* Duplication */
cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
{
return cJSON_Duplicate_rec(item, 0, recurse );
}
cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
{ {
cJSON *newitem = NULL; cJSON *newitem = NULL;
cJSON *child = NULL; cJSON *child = NULL;
@ -2784,7 +2791,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
child = item->child; child = item->child;
while (child != NULL) while (child != NULL)
{ {
newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ if(depth >= CJSON_CIRCULAR_LIMIT) {
goto fail;
}
newchild = cJSON_Duplicate_rec(child, ++depth, true); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) if (!newchild)
{ {
goto fail; goto fail;

View File

@ -137,6 +137,12 @@ typedef int cJSON_bool;
#define CJSON_NESTING_LIMIT 1000 #define CJSON_NESTING_LIMIT 1000
#endif #endif
/* Limits the length of circular references can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000
#endif
/* returns the version of cJSON as a string */ /* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void); CJSON_PUBLIC(const char*) cJSON_Version(void);

View File

@ -219,6 +219,23 @@ static void cjson_should_not_parse_to_deeply_nested_jsons(void)
TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(deep_json), "To deep JSONs should not be parsed."); TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(deep_json), "To deep JSONs should not be parsed.");
} }
static void cjson_should_not_follow_too_deep_circular_references(void)
{
cJSON *o = cJSON_CreateArray();
cJSON *a = cJSON_CreateArray();
cJSON *b = cJSON_CreateArray();
cJSON *x;
cJSON_AddItemToArray(o, a);
cJSON_AddItemToArray(a, b);
cJSON_AddItemToArray(b, o);
x = cJSON_Duplicate(o, 1);
TEST_ASSERT_NULL(x);
cJSON_DetachItemFromArray(b, 0);
cJSON_Delete(o);
}
static void cjson_set_number_value_should_set_numbers(void) static void cjson_set_number_value_should_set_numbers(void)
{ {
cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}}; cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}};
@ -777,6 +794,7 @@ int CJSON_CDECL main(void)
RUN_TEST(cjson_get_object_item_case_sensitive_should_not_crash_with_array); RUN_TEST(cjson_get_object_item_case_sensitive_should_not_crash_with_array);
RUN_TEST(typecheck_functions_should_check_type); RUN_TEST(typecheck_functions_should_check_type);
RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons); RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons);
RUN_TEST(cjson_should_not_follow_too_deep_circular_references);
RUN_TEST(cjson_set_number_value_should_set_numbers); RUN_TEST(cjson_set_number_value_should_set_numbers);
RUN_TEST(cjson_detach_item_via_pointer_should_detach_items); RUN_TEST(cjson_detach_item_via_pointer_should_detach_items);
RUN_TEST(cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null); RUN_TEST(cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null);