cJSON
cJSON copied to clipboard
An infinite loop found in cJSON_DeleteItemFromObjectCaseSensitive
Hi, when fuzzing cJSON, there is a timeout reported by ASAN. There might is an infinite loop bug in cJSON_DeleteItemFromObjectCaseSensitive.
==617073== ERROR: libFuzzer: timeout after 241 seconds
#0 0x5583f5cf2a41 in __sanitizer_print_stack_trace /work/llvm/llvm-project/compiler-rt/lib/asan/asan_stack.cpp:87:3
#1 0x5583f5c0c808 in fuzzer::PrintStackTrace() /work/llvm/llvm-project/compiler-rt/lib/fuzzer/FuzzerUtil.cpp:210:5
#2 0x5583f5befcd9 in fuzzer::Fuzzer::AlarmCallback() /work/llvm/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:301:5
#3 0x7fd7e37c041f (/lib/x86_64-linux-gnu/libpthread.so.0+0x1441f) (BuildId: 7b4536f41cdaa5888408e82d0836e33dcf436466)
#4 0x5583f5c7b995 in __interceptor_strcmp /work/llvm/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:527
#5 0x5583f5d74174 in get_object_item cjson/src/cjson/cJSON.c:1892:83
#6 0x5583f5d7809c in cJSON_GetObjectItemCaseSensitive /cjson/src/cjson/cJSON.c:1919:12
#7 0x5583f5d7809c in cJSON_DetachItemFromObjectCaseSensitive /cjson/src/cjson/cJSON.c:2247:24
#8 0x5583f5d7809c in cJSON_DeleteItemFromObjectCaseSensitive /cjson/src/cjson/cJSON.c:2259:18
My environment: CentOS 5.4
PoC: poc.tar.gz
Interesting find. Basically, whats causing the infinite loop here is a circular reference after adding the same item twice.
I boiled the POC down a bit:
cJSON* root = cJSON_CreateObject();
cJSON* item = cJSON_CreateNumber(42);
cJSON_AddItemToObject(root, "item1", item);
cJSON_AddItemToObject(root, "item2", item); // Circular reference is created here
cJSON_Print(root); // This will enter an infinite loop
Specifically, the circular reference is created in suffix_object
, which is called with prev
and item
being equal.
(https://github.com/DaveGamble/cJSON/blob/cb8693b058ba302f4829ec6d03f609ac6f848546/cJSON.c#L1928C13-L1928C19)
But aside from this analysis, I'm not sure what the best course of action on this would be.
Adding the same item to object for more than one time is a broken use of cJSON_AddItemToObject
. Simply doing same pointer check in suffix_object
will not help much. POC to a similar problem:
cJSON* root = cJSON_CreateObject();
cJSON* item1 = cJSON_CreateNumber(42);
cJSON* item2 = cJSON_CreateNumber(42);
cJSON_AddItemToObject(root, "item1", item1);
cJSON_AddItemToObject(root, "item2", item2);
cJSON_AddItemToObject(root, "item3", item1); // circular reference
cJSON_Print(root);
To prevent this, we need to check all items in a object/array if the added item already exist when adding a item to a object/array. No doubt this will cause performance problems.
I can't find a elegant enough solution. Ideas?