aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Badoux <n.badoux@hotmail.com>2024-08-30 13:36:22 +0200
committerAlan Wang <wp_scut@163.com>2024-09-23 19:08:58 +0800
commit9d1b229086a7b069fb5f4e3be0226a22480a6707 (patch)
tree812676bd307cdad10eda47e224d25b47318062a7
parent078c4e6c53f13dff15f0eaac1611abb6379e0206 (diff)
downloadcjson-9d1b229086a7b069fb5f4e3be0226a22480a6707.tar.gz
cjson-9d1b229086a7b069fb5f4e3be0226a22480a6707.zip
Added max recusrion depth for cJSONDuplicate to prevent stack exhaustion in case of circular reference
-rw-r--r--cJSON.c12
-rw-r--r--cJSON.h6
-rw-r--r--tests/misc_tests.c18
3 files changed, 35 insertions, 1 deletions
diff --git a/cJSON.c b/cJSON.c
index 56f65ef..9399c0d 100644
--- a/cJSON.c
+++ b/cJSON.c
@@ -2737,8 +2737,15 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int co
}
/* 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)
{
+ return cJSON_Duplicate_rec(item, 0, recurse );
+}
+
+cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
+{
cJSON *newitem = NULL;
cJSON *child = NULL;
cJSON *next = NULL;
@@ -2784,7 +2791,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
child = item->child;
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)
{
goto fail;
diff --git a/cJSON.h b/cJSON.h
index 88cf0bc..37520bb 100644
--- a/cJSON.h
+++ b/cJSON.h
@@ -137,6 +137,12 @@ typedef int cJSON_bool;
#define CJSON_NESTING_LIMIT 1000
#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 */
CJSON_PUBLIC(const char*) cJSON_Version(void);
diff --git a/tests/misc_tests.c b/tests/misc_tests.c
index 606b460..a96c2fd 100644
--- a/tests/misc_tests.c
+++ b/tests/misc_tests.c
@@ -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.");
}
+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)
{
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(typecheck_functions_should_check_type);
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_detach_item_via_pointer_should_detach_items);
RUN_TEST(cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null);