4

I'm experimenting memory errors in my C application and using valgrind, I see many strange things around the json-c lib.

So looking at some infos on the web, I saw this post about json_object_new_object

So I have 2 questions to be clear about everything: The first one is about json construction

When I add an object to another object, do I have to just free the first one ? example:

json_object * jobj1 = json_object_new_object();
json_object * jobj2 = json_object_new_object();

json_object_object_add(jobj1,"Object", jobj2);

...

json_object_put(jobj); // Is it enough to free all the json tree??

According to this post it seems to be ok, but it hasn't been really answered.

Question 2: Looking at this tuto, it seems not necessary to free anything but looking at my valgrind log, json_object_new_object is called:

by 0x4F7F4CF: json_object_new_object (in /lib/x86_64-linux-gnu/libjson-c.so.3.0.1)
by 0x4F81B38: json_tokener_parse_ex (in /lib/x86_64-linux-gnu/libjson-c.so.3.0.1)
by 0x4F82316: json_tokener_parse_verbose (in /lib/x86_64-linux-gnu/libjson-c.so.3.0.1)
by 0x4F8237D: json_tokener_parse (in /lib/x86_64-linux-gnu/libjson-c.so.3.0.1)

So do I have to do this ?

json_object *jobj = json_tokener_parse(...);

...

json_object_put(jobj);
fralbo
  • 2,534
  • 4
  • 41
  • 73

1 Answers1

4

The documentation for json_object_new_object says

Remember, when using json_object_object_add or json_object_array_put_idx, ownership will transfer to the object/array.

and

Any ownerships you acquired but did not transfer must be released through json_object_put.

To me, that means this should be okay:

json_object *jobj1 = json_object_new_object();
json_object *jobj2 = json_object_new_object();

json_object_object_add(jobj1, "Object", jobj2);

// ...

json_object_put(jobj1); // This is the only one we have ownership of now

And yes, like the documentation says, you must release all your ownerships using json_object_put.

Regarding json_tokener_parse, the documentation doesn't explicitly say anything about ownership or releasing, but I would be very surprised if it was different json_object_new_object in that respect. It wouldn't make any sense if you weren't allowed to use objects returned from json_tokener_parse the same way you can use objects constructed through json_object_new_object.

For instance, it should be possible to do this:

json_object *jobj1 = json_object_new_object();  // "Manual" object
json_object *jobj2 = json_tokener_parse(...);   // Parsed object

// At this point, jobj1->_ref_count and jobj2->_ref_count will both be 1.

// We could now add the parsed object as a new field in our "manual" one:
json_object_object_add(jobj1, "ParsedObject", jobj2); 

// ...

// In the end, we only have to release the "manual" object.
json_object_put(jobj1);

Also, yes, as your valgrind log indicates, the json_tokener_parse function calls json_object_new_object behind the scenes, so you need to call json_object_put for the returned object (i.e., unless you incorporate it into another object or array, as mentioned earlier).

The guy who wrote the code in the referenced blog post doesn't seem to care about the memory leak in his program, perhaps because that program is so simple and ends immediately after processing only one object.

mbj
  • 987
  • 5
  • 18
  • many thanks for your answer. Do you also confirm that `json_object_put` must be called when we have finished with `json_tokener_parse`? – fralbo Jan 15 '19 at 16:38
  • @fralbo: Yes, I have updated my answer to also address the part about json_tokener_parse. I have also verified my claims by actually looking at the source code for json-c at https://github.com/json-c/json-c – mbj Jan 15 '19 at 18:15
  • Please don't use the doc links mentioned above, start from http://json-c.github.io/json-c/ instead. – Eric Jul 09 '20 at 20:14