0

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
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jeremy Whitcher
  • 611
  • 7
  • 10

2 Answers2

0

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);
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jeremy Whitcher
  • 611
  • 7
  • 10
0

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.

Danny
  • 1,603
  • 1
  • 15
  • 25