From: "Daniel P. Berrangé" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Paolo Bonzini" <pbonzini@redhat.com>,
"Daniel P. Berrangé" <berrange@redhat.com>
Subject: [PATCH 8/9] qom: fix ability to create objects without a parent
Date: Wed, 29 Apr 2026 22:08:29 +0100 [thread overview]
Message-ID: <20260429210830.594724-9-berrange@redhat.com> (raw)
In-Reply-To: <20260429210830.594724-1-berrange@redhat.com>
object_new_with_propv allowed id/parent to be optional, in which case
the caller was expected to own the returned object. Unfortunately a
trailing object_unref() meant that the returned object was already
freed.
It is confusing to have a single method with two different ownership
scenarios for the returned object.
Make id/parent mandatory in object_new_with_propv once more, and add
a new object_new_with_propv_parentless that does not accept id/parent
at all and lets the caller own the returned reference.
The helper method has abstracted the way properties are represented
and setk in order to facilitate the subsequent commit.
Unit tests are added to address the root cause that allowed the bug
to slip through in commit 6134d752.
Fixes: 6134d7522e570a30d7f0d1e092ee37351c5183ed
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
include/qom/object.h | 47 ++++++++++++++++++
qom/object.c | 74 +++++++++++++++++++++++----
tests/unit/check-qom-proplist.c | 88 ++++++++++++++++++++++++++++-----
3 files changed, 187 insertions(+), 22 deletions(-)
diff --git a/include/qom/object.h b/include/qom/object.h
index cd59f1f171..71530dac76 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -719,6 +719,53 @@ Object *object_new_with_props_from_qdict(const char *typename,
Visitor *v,
Error **errp);
+/**
+ * object_new_with_props_parentless:
+ * @typename: The name of the type of the object to instantiate.
+ * @errp: pointer to error object
+ * @...: list of property names and values
+ *
+ * Behaviour as object_new_with_props(), except the object
+ * will not be added to any parent and thus the caller will
+ * own the returned instance. The caller must call
+ * object_unref when it is no longer required.
+ */
+Object *object_new_with_props_parentless(const char *typename,
+ Error **errp,
+ ...) G_GNUC_NULL_TERMINATED;
+
+/**
+ * object_new_with_propv_parentless:
+ * @typename: The name of the type of the object to instantiate.
+ * @vargs: list of property names and values
+ * @errp: pointer to error object
+ *
+ * Behaviour as object_new_with_propv(), except the object
+ * will not be added to any parent and thus the caller will
+ * own the returned instance. The caller must call
+ * object_unref when it is no longer required.
+ */
+Object *object_new_with_propv_parentless(const char *typename,
+ va_list vargs,
+ Error **errp);
+
+/**
+ * object_new_with_props_from_qdict_parentless:
+ * @typename: The name of the type of the object to instantiate.
+ * @props: dictionary of property names and values
+ * @v: visitor to iterate over @props
+ * @errp: pointer to error object
+ *
+ * Behaviour as object_new_with_props_from_qdict(), except the
+ * object will not be added to any parent and thus the caller
+ * will own the returned instance. The caller must call
+ * object_unref when it is no longer required.
+ */
+Object *object_new_with_props_from_qdict_parentless(const char *typename,
+ QDict *props,
+ Visitor *v,
+ Error **errp);
+
/**
* object_set_props:
* @obj: the object instance to set properties on
diff --git a/qom/object.c b/qom/object.c
index b35a3dff06..9dc2bdb603 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -750,6 +750,8 @@ Object *object_new_with_props(const char *typename,
va_list vargs;
Object *obj;
+ assert(parent != NULL);
+ assert(id != NULL);
va_start(vargs, errp);
obj = object_new_with_propv(typename, parent, id, vargs, errp);
va_end(vargs);
@@ -767,10 +769,14 @@ object_new_with_props_helper(const char *typename,
Error **errp),
Error **errp)
{
+ ERRP_GUARD();
Object *obj;
ObjectClass *klass;
UserCreatable *uc;
+ assert((id != NULL && parent != NULL) ||
+ (id == NULL && parent == NULL));
+
if (id != NULL && !id_wellformed(id)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
error_append_hint(errp, "Identifiers consist of letters, digits, "
@@ -795,7 +801,10 @@ object_new_with_props_helper(const char *typename,
}
if (id != NULL) {
- object_property_add_child(parent, id, obj);
+ object_property_try_add_child(parent, id, obj, errp);
+ if (*errp) {
+ goto error;
+ }
}
uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE);
@@ -808,7 +817,6 @@ object_new_with_props_helper(const char *typename,
}
}
- object_unref(obj);
return obj;
error:
@@ -836,7 +844,8 @@ Object *object_new_with_propv(const char *typename,
{
Object *obj;
struct ObjectNewVargsData data;
-
+ assert(parent != NULL);
+ assert(id != NULL);
va_copy(data.vargs, vargs);
obj = object_new_with_props_helper(typename,
parent,
@@ -845,6 +854,9 @@ Object *object_new_with_propv(const char *typename,
object_new_with_propv_setter,
errp);
va_end(data.vargs);
+ if (obj) {
+ object_unref(obj);
+ }
return obj;
}
@@ -869,12 +881,56 @@ Object *object_new_with_props_from_qdict(const char *typename,
Error **errp)
{
struct ObjectNewQDictData data = { props, v };
- return object_new_with_props_helper(typename,
- parent,
- id,
- &data,
- object_new_with_qdict_setter,
- errp);
+ Object *obj;
+ assert(parent != NULL);
+ assert(id != NULL);
+ obj = object_new_with_props_helper(typename,
+ parent,
+ id,
+ &data,
+ object_new_with_qdict_setter,
+ errp);
+ if (obj) {
+ object_unref(obj);
+ }
+ return obj;
+}
+
+Object *object_new_with_props_parentless(const char *typename,
+ Error **errp,
+ ...)
+{
+ va_list vargs;
+ Object *obj;
+
+ va_start(vargs, errp);
+ obj = object_new_with_propv_parentless(typename, vargs, errp);
+ va_end(vargs);
+
+ return obj;
+}
+
+Object *object_new_with_propv_parentless(const char *typename,
+ va_list vargs,
+ Error **errp)
+{
+ Object *ret;
+ struct ObjectNewVargsData data;
+ va_copy(data.vargs, vargs);
+ ret = object_new_with_props_helper(typename, NULL, NULL, &data,
+ object_new_with_propv_setter, errp);
+ va_end(vargs);
+ return ret;
+}
+
+Object *object_new_with_props_from_qdict_parentless(const char *typename,
+ QDict *props,
+ Visitor *v,
+ Error **errp)
+{
+ struct ObjectNewQDictData data = { props, v };
+ return object_new_with_props_helper(typename, NULL, NULL, &data,
+ object_new_with_qdict_setter, errp);
}
bool object_set_props(Object *obj,
diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c
index 7f31735459..954c898ce1 100644
--- a/tests/unit/check-qom-proplist.c
+++ b/tests/unit/check-qom-proplist.c
@@ -336,7 +336,7 @@ static QemuOptsList qemu_object_opts = {
};
-static void test_dummy_createv(void)
+static void test_dummy_createv_tree(void)
{
Error *err = NULL;
Object *parent = object_get_objects_root();
@@ -351,6 +351,7 @@ static void test_dummy_createv(void)
NULL));
g_assert(err == NULL);
+ g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
g_assert(dobj->bv == true);
g_assert(dobj->av == DUMMY_PLATYPUS);
@@ -362,9 +363,30 @@ static void test_dummy_createv(void)
}
-static Object *new_helper(Error **errp,
- Object *parent,
- ...)
+static void test_dummy_createv_parentless(void)
+{
+ Error *err = NULL;
+ DummyObject *dobj = DUMMY_OBJECT(
+ object_new_with_props_parentless(TYPE_DUMMY,
+ &err,
+ "bv", "yes",
+ "sv", "Hiss hiss hiss",
+ "av", "platypus",
+ NULL));
+
+ g_assert(err == NULL);
+ g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
+ g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
+ g_assert(dobj->bv == true);
+ g_assert(dobj->av == DUMMY_PLATYPUS);
+
+ object_unref(OBJECT(dobj));
+}
+
+
+static Object *new_helper_tree(Error **errp,
+ Object *parent,
+ ...)
{
va_list vargs;
Object *obj;
@@ -379,19 +401,20 @@ static Object *new_helper(Error **errp,
return obj;
}
-static void test_dummy_createlist(void)
+static void test_dummy_createlist_tree(void)
{
Error *err = NULL;
Object *parent = object_get_objects_root();
DummyObject *dobj = DUMMY_OBJECT(
- new_helper(&err,
- parent,
- "bv", "yes",
- "sv", "Hiss hiss hiss",
- "av", "platypus",
- NULL));
+ new_helper_tree(&err,
+ parent,
+ "bv", "yes",
+ "sv", "Hiss hiss hiss",
+ "av", "platypus",
+ NULL));
g_assert(err == NULL);
+ g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
g_assert(dobj->bv == true);
g_assert(dobj->av == DUMMY_PLATYPUS);
@@ -402,6 +425,39 @@ static void test_dummy_createlist(void)
object_unparent(OBJECT(dobj));
}
+static Object *new_helper_parentless(Error **errp,
+ ...)
+{
+ va_list vargs;
+ Object *obj;
+
+ va_start(vargs, errp);
+ obj = object_new_with_propv_parentless(TYPE_DUMMY,
+ vargs,
+ errp);
+ va_end(vargs);
+ return obj;
+}
+
+static void test_dummy_createlist_parentless(void)
+{
+ Error *err = NULL;
+ DummyObject *dobj = DUMMY_OBJECT(
+ new_helper_parentless(&err,
+ "bv", "yes",
+ "sv", "Hiss hiss hiss",
+ "av", "platypus",
+ NULL));
+
+ g_assert(err == NULL);
+ g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
+ g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
+ g_assert(dobj->bv == true);
+ g_assert(dobj->av == DUMMY_PLATYPUS);
+
+ object_unref(OBJECT(dobj));
+}
+
static bool test_create_obj(QDict *qdict, Error **errp)
{
Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
@@ -658,8 +714,14 @@ int main(int argc, char **argv)
type_register_static(&dummy_bus_info);
type_register_static(&dummy_backend_info);
- g_test_add_func("/qom/proplist/createlist", test_dummy_createlist);
- g_test_add_func("/qom/proplist/createv", test_dummy_createv);
+ g_test_add_func("/qom/proplist/createlist/tree",
+ test_dummy_createlist_tree);
+ g_test_add_func("/qom/proplist/createlist/parentless",
+ test_dummy_createlist_parentless);
+ g_test_add_func("/qom/proplist/createv/tree",
+ test_dummy_createv_tree);
+ g_test_add_func("/qom/proplist/createv/parentless",
+ test_dummy_createv_parentless);
g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl);
g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
--
2.54.0
next prev parent reply other threads:[~2026-04-29 21:10 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-29 21:08 [PATCH 0/9] qom: misc cleanups / fixes Daniel P. Berrangé
2026-04-29 21:08 ` [PATCH 1/9] qom: add trace events for object/property lifecycle Daniel P. Berrangé
2026-04-29 21:40 ` marcandre.lureau
2026-04-30 7:56 ` Daniel P. Berrangé
2026-04-29 21:08 ` [PATCH 2/9] qom: validate ID format when creating objects Daniel P. Berrangé
2026-04-29 21:08 ` [PATCH 3/9] qom: make errp last param in methods taking va_list Daniel P. Berrangé
2026-04-30 8:14 ` Philippe Mathieu-Daudé
2026-04-29 21:08 ` [PATCH 4/9] qom: shorten name of object_set_properties_from_keyval Daniel P. Berrangé
2026-04-30 8:14 ` Philippe Mathieu-Daudé
2026-04-29 21:08 ` [PATCH 5/9] qom: have object_set_props_keyval return bool Daniel P. Berrangé
2026-04-29 21:08 ` [PATCH 6/9] qom: move object_set_prop_keyval into object.c Daniel P. Berrangé
2026-04-29 21:40 ` marcandre.lureau
2026-04-29 21:08 ` [PATCH 7/9] qom: add object_new_with_props_from_qdict Daniel P. Berrangé
2026-04-29 21:08 ` Daniel P. Berrangé [this message]
2026-04-29 21:08 ` [PATCH 9/9] qom: drop user_creatable_add_type method Daniel P. Berrangé
2026-04-29 21:40 ` marcandre.lureau
2026-04-30 7:54 ` Daniel P. Berrangé
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260429210830.594724-9-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.