]> git.feebdaed.xyz Git - 0xmirror/cJSON.git/commitdiff
Added max recusrion depth for cJSONDuplicate to prevent stack exhaustion in case...
authorNicolas Badoux <n.badoux@hotmail.com>
Fri, 30 Aug 2024 11:36:22 +0000 (13:36 +0200)
committerAlan Wang <wp_scut@163.com>
Mon, 23 Sep 2024 11:08:58 +0000 (19:08 +0800)
cJSON.c
cJSON.h
tests/misc_tests.c

diff --git a/cJSON.c b/cJSON.c
index 56f65efe4416641a1e8dd983451f6ba10c4823f5..9399c0ddeeed7b7e2051a8e76aeee75783eda36e 100644 (file)
--- a/cJSON.c
+++ b/cJSON.c
@@ -2737,7 +2737,14 @@ 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;
@@ -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 88cf0bcfa274fb686a989ba77f3b16d1aa78be67..37520bbcf87988b013dd6656983caf55f4e8a19d 100644 (file)
--- 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);
 
index 606b4603449cfcfad3539ddb23b30944f03c949b..a96c2fdc04c5263f0eadf92ee229830d4ea0b8a8 100644 (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.");
 }
 
+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);