From 73b4f5d3e25d0ecfafd390a880365ffa858f0e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Mon, 24 Aug 2020 17:00:50 +0100 Subject: Fix copy behavior for complex types The parent of the reffed children were previously left pointing to the original node. In the best-case scenario this would lead to inconsistent state when walking down a tree and then walking back up again. In the worst-case scenario this would happen when the original node had a shorter life-time than the copy, resulting in use-after-free. A typical scenario where this went wrong was with json_from_string(), which would copy the root node, let go of the last reference to the root node, and then return the copy. The copy would then have dangling `parent` pointers. This probably went unnoticed for most use-cases, but would go terribly wrong if someone used a JsonReader and navigated back up the tree by e.g. calling end_member(). Fixes: #20 Fixes: #32 --- json-glib/json-object.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'json-glib/json-object.c') diff --git a/json-glib/json-object.c b/json-glib/json-object.c index 8e6b293..4335256 100644 --- a/json-glib/json-object.c +++ b/json-glib/json-object.c @@ -72,6 +72,35 @@ json_object_new (void) return object; } +JsonObject * +json_object_copy (JsonObject *object, + JsonNode *new_parent) +{ + JsonObject *copy; + GList *cur; + + copy = json_object_new (); + + for (cur = object->members_ordered.head; cur != NULL; cur = cur->next) + { + gchar *name; + JsonNode *child_copy; + + name = g_strdup (cur->data); + + child_copy = json_node_copy (g_hash_table_lookup (object->members, name)); + child_copy->parent = new_parent; + + g_hash_table_insert (copy->members, name, child_copy); + g_queue_push_tail (©->members_ordered, name); + } + + copy->immutable_hash = object->immutable_hash; + copy->immutable = object->immutable; + + return copy; +} + /** * json_object_ref: * @object: a #JsonObject -- cgit v1.2.1