Skip to content

cJSONUtils_ApplyPatches* dereferences NULL object on root-level add/replace #1010

@Yanhaoxi

Description

@Yanhaoxi

Summary

cJSONUtils_ApplyPatches() and cJSONUtils_ApplyPatchesCaseSensitive() do not validate that the object argument is non-NULL before passing it to apply_patch().

When a patch targets the root path ("") with an add or replace operation, this leads to an unconditional dereference of object, causing a crash.

Affected version

  • cJSON 1.7.19
  • Code path: cJSONUtils_ApplyPatches() / cJSONUtils_ApplyPatchesCaseSensitive()

Details

The public API forwards object directly to apply_patch() without validation:

CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
{
    ...
    while (current_patch != NULL)
    {
        status = apply_patch(object, current_patch, false);
        ...
    }
}

In apply_patch(), when handling a root-level path (""), object is dereferenced:

if (path->valuestring[0] == '\0')
{
    ...
    if ((opcode == REPLACE) || (opcode == ADD))
    {
        ...
        overwrite_item(object, *value);
        ...
        if (object->string != NULL)
        {
            cJSON_free(object->string);
            object->string = NULL;
        }
    }
}

While overwrite_item() itself tolerates NULL, the subsequent access to object->string does not.

Proof of Concept

#include "cJSON.h"
#include "cJSON_Utils.h"

int main(void)
{
    cJSON *patches = cJSON_CreateArray();
    cJSON *patch = cJSON_CreateObject();

    cJSON_AddItemToObject(patch, "op", cJSON_CreateString("add"));
    cJSON_AddItemToObject(patch, "path", cJSON_CreateString(""));
    cJSON_AddItemToObject(patch, "value", cJSON_CreateNumber(1));
    cJSON_AddItemToArray(patches, patch);

    return cJSONUtils_ApplyPatches(NULL, patches);
}

Build and run

gcc poc_object_null.c cJSON.c cJSON_Utils.c -o poc_object_null
./poc_object_null

On Windows with MinGW, this results in an access violation (0xC0000005).

Expected behavior

The API should handle invalid input gracefully. Passing a NULL object should not result in a crash; instead, the function should return an error code.

Suggested fix

One option is to validate the input at the public API boundary:

if (object == NULL)
{
    return 1;
}

Alternatively, apply_patch() could reject object == NULL before performing any operation-specific logic.

Adding a check at the public entry points would make the API behavior more predictable and consistent with defensive handling of invalid inputs.

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