Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String deallocated before use #248

Closed
hhallen opened this issue Mar 1, 2018 · 4 comments
Closed

String deallocated before use #248

hhallen opened this issue Mar 1, 2018 · 4 comments

Comments

@hhallen
Copy link

hhallen commented Mar 1, 2018

Maybe I'm doing things in a dumb way, but given the method I'm using, there's a problem with the function add_item_to_object(). What I want to do is to parse a number of separate .json files and merge their contents into a single cJSON object. The files may partially duplicate each other. Objects and array items that are already in the result that is being constructed should be skipped during the processing of a .json file, i.e. no duplicate objects or array elements are permitted in the result output.

So, at the top level each file contains one object (always with the same name, "stuff" in the simplified examples below), which in turn contains other objects, which contain arrays of objects. A simple example will illustrate what I want to achieve.

.json file 1:

{
    "stuff": {
        "FOO": [
            {
                "name": "foo_1",
                "value": "11111111"
            },
            {
                "name": "foo_2",
                "value": "11111112"
            }
        ],
        "BAR": [
            {
                "name": "bar_1",
                "value": "22222221"
            },
            {
                "name": "bar_2",
                "value": "22222222"
            }
        ]
    }
}

.json file 2:

{
    "stuff": {
        "FOO": [
            {
                "name": "foo_1",
                "value": "11111111"
            },
            {
                "name": "foo_3",
                "value": "11111113"
            }
        ],
        "BAZ": [
            {
                "name": "baz_1",
                "value": "33333331"
            }
        ]
    }
}

I want the result of the merge operation to be a cJSON object that represents the following .json:

{
    "stuff": {
        "FOO": [
            {
                "name": "foo_1",
                "value": "11111111"
            },
            {
                "name": "foo_2",
                "value": "11111112"
            },
            {
                "name": "foo_3",
                "value": "11111113"
            }
        ],
        "BAR": [
            {
                "name": "bar_1",
                "value": "22222221"
            },
            {
                "name": "bar_2",
                "value": "22222222"
            }
        ],
        "BAZ": [
            {
                "name": "baz_1",
                "value": "33333331"
            }
        ]
    }
}

First, I use cJSON_CreateObject() to start my result object. Then I process each .json file in turn, using cJSON_Parse() to create a cJSON object that represents the file. Then this cJSON object is examined, and if any "stuff" is found, it is moved to the result object, except such objects/array elements as are already present there.

Here is a code snippet that shows how I handle the merge operation for an object at the level directly under "stuff" that is not yet present at all in the result:

            newGroup = cJSON_DetachItemFromObject(newStuff, newGroup->string);
            cJSON_AddItemToObject(resultStuff, newGroup->string, newGroup);

newGroup is a cJSON object from the current .json file, representing for example "BAZ" from the .json files above. I want to move that object from the cJSON object representing the .json file (newStuff) to the cJSON result object under construction (resultStuff).

The problem is that the internal function add_item_to_object() in cJSON assumes that its string argument is valid throughout the function. But when I use newGroup->string, that assumption becomes false, because that string is deallocated before a copy is made and inserted in the item before it's added to the target object. The result (at best) is that the item string will contain garbage.

Perhaps the order of actions in add_item_to_object() could be rearranged, so that a copy of the string argument is made before any deallocations?

Of course, I could allocate memory and make a copy of the string in my own code, but that seems a bit silly. Maybe my method is naive, and a there is a better way of doing what I want that does not run into this kind of problem? If so, please let me know. I'm relatively new to cJSON.

@FSMaxB
Copy link
Collaborator

FSMaxB commented Mar 2, 2018

Maybe cJSONUtils_MergePatchCaseSensitive does what you want. It implements RFC 7396. In this case your objects mustn't contain null though.

Otherwise, use cJSON_DetachItemViaPointer using the newGroup pointer. That will make the code faster because it doesn't need to iterate over all the object items anymore.

Also you can use cJSON_AddItemToArray instead of cJSON_AddItemToObject to get around the use after free poblem. This also avoids the cJSON_strdup.

I still think that it makes sense though to rearrange the order in which the values are accessed. It just never occured to me before that the parameter to cJSON_AddItemToObject could be an alias of the items string property. So I guess I will fix that and write a test for it, so I don't accidentally unfix it.

@FSMaxB
Copy link
Collaborator

FSMaxB commented Mar 2, 2018

Since it is a mistake that could easily happen and there is no warning about it, I consider this a bug. And because of the use-after-free it can potentially have security implications.

I will fix this in a new bugfix release as soon as possible.

FSMaxB added a commit to FSMaxB/cJSON that referenced this issue Mar 2, 2018
If the `string` property of the item that is added is an alias to the
`string` parameter of `add_item_to_object`, and `constant` is false,
`cJSON_strdup` would access the string after it has been freed.

Thanks @hhallen for reporting this in DaveGamble#248.
@FSMaxB
Copy link
Collaborator

FSMaxB commented Mar 2, 2018

The use after free is now fixed in 1.7.4.

This can be closed as soon as your use case of merging multiple JSON objects is figured out.

@hhallen
Copy link
Author

hhallen commented Mar 5, 2018

Thanks for the tips in your first reply, and for fixing the issue so quickly. Unfortunately the 'merge patch' method doesn't seem to quite do what I need (the actual use case is a bit more complex than the example I gave). But the tips about cJSON_DetachItemViaPointer and cJSON_AddItemToArray were useful.

@hhallen hhallen closed this as completed Mar 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants