Skip to content

Array Circular Reference Stack Overflow in cJSON: A Systematic Design Issue #954

@LkkkLxy

Description

@LkkkLxy

Description of the bug

This vulnerability represents part of a systematic design flaw in the cJSON library regarding circular reference handling. The library exhibits inconsistent behavior across its APIs when dealing with circular references, creating a fundamental architectural problem. The specific manifestation here is a stack overflow when printing JSON structures containing circular array references. The cJSON_CreateArrayReference function allows creation of array references that form cycles, but the print functions lack cycle detection for arrays, leading to infinite recursion and stack overflow.

To Reproduce

Here's a minimal example that reproduces the array circular reference stack overflow:

#include "cJSON.h"
#include <stdlib.h>
#include <stdio.h>

int main() {
    // Create root object
    cJSON *root = cJSON_CreateObject();
    
    // Create integer array
    int numbers[] = {1, 2, 3, 4, 5};
    cJSON *int_array = cJSON_CreateIntArray(numbers, 5);
    
    if (int_array) {
        // Add array to root object
        cJSON_AddItemToObject(root, "int_array", int_array);
        
        // Create reference to the same array - this creates a cycle
        cJSON *array_ref = cJSON_CreateArrayReference(int_array);
        if (array_ref) {
            cJSON_AddItemToObject(root, "array_ref", array_ref);
        }
        
        // Add other items
        cJSON_AddTrueToObject(root, "true_value");
        cJSON_AddFalseToObject(root, "false_value");
    }
    
    printf("Attempting to print structure with circular array reference...\n");
    
    // This will cause infinite recursion due to the circular array reference
    char *printed = cJSON_PrintUnformatted(root);
    
    if (printed) {
        free(printed);
    }
    
    cJSON_Delete(root);
    return 0;
}

Steps to reproduce:

  1. Compile the above code with cJSON library and AddressSanitizer
  2. Run the program
  3. Observe the stack overflow crash due to infinite recursion in array printing

Version Information

  • cJSON version: Current master branch
  • OS and version: Linux (tested in Docker container with AddressSanitizer)
  • Compiler: Clang with -fsanitize=address

Stack Trace

ERROR: AddressSanitizer: stack-overflow on address 0x7ffd3988efb8
#3 0x563875d6b131 in print_number /src/cjson/cJSON.c:613:18
#4 0x563875d6b131 in print_value /src/cjson/cJSON.c:1457:20
#5 0x563875d6b27b in print_array /src/cjson/cJSON.c:1615:14
#6 0x563875d6b27b in print_value /src/cjson/cJSON.c:1481:20
#7 0x563875d6b27b in print_array /src/cjson/cJSON.c:1615:14
#8 0x563875d6b27b in print_value /src/cjson/cJSON.c:1481:20
#9 0x563875d6b27b in print_array /src/cjson/cJSON.c:1615:14
#10 0x563875d6b27b in print_value /src/cjson/cJSON.c:1481:20
... [pattern repeats 400+ times]
#488 0x563875d6b27b in print_array /src/cjson/cJSON.c:1615:14
#489 0x563875d6b27b in print_value /src/cjson/cJSON.c:1481:20

SUMMARY: AddressSanitizer: stack-overflow /src/cjson/cJSON.c:613:18 in print_number

Discussion

In #880 and #882 , I observed similar issues. Although these pertain to different APIs, the underlying problem with circular references is analogous.

You mentioned that “cJSON is designed to believe the input arguments are correct” and “we are aware of circular references,” which implies that cJSON acknowledges the issue but considers it the responsibility of the user to prevent it.

However, I noticed that #888
partially addresses circular dependency issues for some other APIs, yet no circular dependency detection has been added to many of the remaining APIs.

In summary, my observation is as follows:

If circular dependency issues are considered the user’s responsibility, then this PR appears largely meaningless. Conversely, if cJSON should guarantee safety against circular dependencies, then applying the approach of this PR would require implementing similar protections across all APIs—an endeavor with significant cost. Moreover, in my view, the modifications in this PR alone do not resolve the underlying architectural problem.

I would like to know whether you agree with this perspective, and whether you see a feasible solution. I understand that a comprehensive solution may involve considerable effort, but I am still eager to discuss this with you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions