简体   繁体   中英

Segmentation Fault when Modifying a cJSON Struct Created by Parsing a String Literal

When using cJSON to parse a string literal I was getting a segmentation fault when free'ing the cJSON structure.

The original code was as follows:

char* jsonStr = "{ \"command\" : { \"param1\": \"value1\", \"param2\": \"value2\" } }";
cJSON *jsonMsg = cJSON_Parse(jsonStr);
cJSON *command = CJSON_GetObjectItem(jsonMsg, "command");
cJSON_GetObjectItem(command,"param1")->valuestring = "new value 1";
cJSON_Delete(jsonMsg); // <— segmentation fault

When I first encountered this I was puzzled by the behavior. The example is very similar to the one in the cJSON documentation .

My first attempt at a solution was to set the type of "param1" so that the cJSON_Delete() function wouldn't try to free the memory. That is, set the "cJSON_IsReference" flag in the cJSON->type member.

The updated code was:

char* jsonStr = "{ \"command\" : { \"param1\": \"value1\", \"param2\": \"value2\" } }";
cJSON *jsonMsg = cJSON_Parse(jsonStr);
cJSON *command = CJSON_GetObjectItem(jsonMsg, "command");
cJSON_GetObjectItem(command,"param1")->valuestring = "new value 1";
cJSON_GetObjectItem(command,"param1")->type |= cJSON_IsReference;
cJSON_Delete(jsonMsg);

The final solution was to transfer the contents of the original message into a new cJSON object. This prevented a memory leak due to orphaned memory malloc'd by cJSON_Parse().

The final code looked like this:

char* jsonStr = "{ \"command\" : { \"param1\": \"value1\", \"param2\": \"value2\" } }";
cJSON *jsonMsg = cJSON_Parse(jsonStr);
cJSON *command = CJSON_GetObjectItem(jsonMsg, "command");

cJSON *jsonRes, *command;
jsonRes = cJSON_CreateObject();
command = cJSON_CreateObject()
cJSON_AddItemToObject(jsonRes, "command", command);
cJSON_AddStringToObject(command, cJSON_GetObjectItem(command,"param1")->string, "new value 1");
cJSON_AddItemToObject(jsonRes, "command", command = cJSON_CreateObject());
cJSON_AddStringToObject(command, 
    cJSON_GetObjectItem(command,"param2")->string, 
    cJSON_GetObjectItem(command,"param2")->valuestring);

cJSON_Print(jsonRes);

cJSON_Delete(jsonMsg);
cJSON_Delete(jsonRes);

cJSON is a very nice lib, simple and neat, but one need to understand some things:

cJSON_GetObjectItem(command,"param1")->valuestring

is a char * , after parsing in this example.

Since you replace it with "new value 1" , being a const char * , so when deleting the jsonMsg , the delete command tries to free that const char * , resulting in a segmentation fault.

there are a couple of approaches:

char* jsonStr = "{ \"command\" : { \"param1\": \"value1\", \"param2\": \"value2\" } }";
cJSON *jsonMsg = cJSON_Parse(jsonStr);
cJSON *command = CJSON_GetObjectItem(jsonMsg, "command");

till here alright,

then one simple command:

cJSON_ReplaceItemInObject(command,"param1", cJSON_CreateString("new value 1"));

and Finish :

cJSON_Print(jsonMsg);
cJSON_Delete(jsonMsg);

Or

cJSON_DeleteItemFromObject(command,"param1");
cJSON_AddItemToObject(command,"param1",cJSON_CreateString("new value 1"));

Or

If you insist on manually manipulating, ok:

free(cJSON_GetObjectItem(command,"param1")->value string);
cJSON_GetObjectItem(command,"param1")->valuestring=strdup("new value 1");

but if you manipulate manually, one should check the type cJSON_IsReference before trying to free, secondly the strdup will allocate new memory to copy the "new value 1" in.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM