From nobody Sun Jul 26 13:30:35 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=reject dis=none) header.from=rsg.ci.i.u-tokyo.ac.jp Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 178149683870551.304091829358185; Sun, 14 Jun 2026 21:13:58 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wYyhU-0000jU-8L; Mon, 15 Jun 2026 00:13:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wYygk-0000dE-8D; Mon, 15 Jun 2026 00:12:47 -0400 Received: from www3579.sakura.ne.jp ([49.212.243.89]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wYygf-0002Ut-LK; Mon, 15 Jun 2026 00:12:41 -0400 Received: from h183.csg.ci.i.u-tokyo.ac.jp (h183.csg.ci.i.u-tokyo.ac.jp [133.11.54.183]) (authenticated bits=0) by www3579.sakura.ne.jp (8.16.1/8.16.1) with ESMTPSA id 65F4CEdf067027 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Mon, 15 Jun 2026 13:12:19 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=0eqFwQkqtR+nEulRgm3tM2eTrUW+AhUBLNz94eQVOCg=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1781496739; v=1; b=rXn2/kcDS7Ecy55+EEIz+fg14MSKj6taMc8iuOFZ0hiD7TtgHIzu5BjrxK1e2CTA Ltrt1jyNNo+6sJQFmll+kIcU0s7aKO2wu0O2utpaRFudiQ1dRjM8nggXyYng6rWF m4tRx7Nw/xZn0N4MXdeCuW+HXs2dPK91tCIohB6JApP2VEL66qB/3FBcny3nZZpM cvR+TpXvDzSMQFbtTIwQQzmX8NC7jbaeiCqBlwiUk2GUG1unw9EphW/XnMDOI4JV TnyjFrrqavBspLRREH54AjeqEZd5kwSl0Wtk2q4MBqh67bDAW846Lh+yopB/GYpK SvVpsDIJiaCgR5/qfSSBTg== From: Akihiko Odaki Date: Mon, 15 Jun 2026 13:11:06 +0900 Subject: [PATCH 1/2] qom: Reject temporary object resurrection MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260615-embedded-v1-1-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp> References: <20260615-embedded-v1-0-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260615-embedded-v1-0-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Manos Pitsidianakis , qemu-rust@nongnu.org, Peter Xu , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Alberto Garcia , Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Akihiko Odaki X-Mailer: b4 0.16-dev-16047 X-Developer-Signature: v=1; a=openpgp-sha256; l=6155; i=odaki@rsg.ci.i.u-tokyo.ac.jp; h=from:subject:message-id; bh=PeveLOKGd+DH/AQHOXNpkUVbiSMs+UHfGxxm2/E25SQ=; b=owGbwMvMwCWmMbc20y1CyJDxtFoSQ5Z+9ULvu5GP78tdm2Ru9OvCMfW96fdN1qx8eqSv1dFik pSQyB+mjlIWBjEuBlkxRZaUot3cGtG1nwoT4ltg5rAygQxh4OIUgIlwXWP4p/E4a+/qe0lOCzsb ny0+HqXnqRueKnv4p+p0sdqqWK6SVQx/Bbu4zlcKpLbv9TPjy+j8Lzzv9tmfs6dKhkntemTxy38 WGwA= X-Developer-Key: i=odaki@rsg.ci.i.u-tokyo.ac.jp; a=openpgp; fpr=AEDC03C9AF734F2EC26A7BFFA4BAEAA73536753C Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=49.212.243.89; envelope-from=odaki@rsg.ci.i.u-tokyo.ac.jp; helo=www3579.sakura.ne.jp X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1781496842054158500 If object_ref() is called during finalization, it will temporarily "resurrect" the object. Although object_finalize() asserts that no resurrecting reference remains before freeing the object, the assertion cannot catch the case where the resurrecting reference is dropped before freeing the object. One way to catch this would be to add a check in object_ref() to reject resurrecting references before they are created. However, object_ref() is frequently called so it is better to minimize the overhead. To avoid adding the overhead, change how the reference count is represented and let an existing assertion detect resurrection. More concretely, obj->ref now stores the current reference count minus 1. The stored count is therefore 0 after object_initialize(), and the final object_unref() decrements it from 0 to UINT32_MAX and starts finalization. A resurrecting object_ref() will then trip the existing obj->ref < INT_MAX assertion. Signed-off-by: Akihiko Odaki --- include/qom/object.h | 4 ++-- block/throttle-groups.c | 2 +- qom/object.c | 15 ++++++--------- tests/unit/check-qom-proplist.c | 8 ++++---- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 11f55613fcd0..c4dcf65dcf7f 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1121,7 +1121,7 @@ GSList *object_class_get_list_sorted(const char *impl= ements_type, * @obj: the object * * Increase the reference count of a object. A object cannot be freed as = long - * as its reference count is greater than zero. + * as a reference remains. * Returns: @obj */ Object *object_ref(void *obj); @@ -1131,7 +1131,7 @@ Object *object_ref(void *obj); * @obj: the object * * Decrease the reference count of a object. A object cannot be freed as = long - * as its reference count is greater than zero. + * as a reference remains. */ void object_unref(void *obj); =20 diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 4b1b1944c20a..3b2a89f5849d 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -962,7 +962,7 @@ static void throttle_group_get_limits(Object *obj, Visi= tor *v, =20 static bool throttle_group_can_be_deleted(UserCreatable *uc) { - return OBJECT(uc)->ref =3D=3D 1; + return OBJECT(uc)->ref =3D=3D 0; } =20 static void throttle_group_obj_class_init(ObjectClass *klass, diff --git a/qom/object.c b/qom/object.c index 0ac201de4c15..761eff1337e6 100644 --- a/qom/object.c +++ b/qom/object.c @@ -497,7 +497,6 @@ static void object_initialize_with_type(Object *obj, si= ze_t size, TypeImpl *type =20 memset(obj, 0, type->instance_size); obj->class =3D type->class; - object_ref(obj); object_class_property_init_all(obj); obj->properties =3D g_hash_table_new_full(g_str_hash, g_str_equal, NULL, object_property_free); @@ -560,11 +559,10 @@ bool object_initialize_child_with_propsv(Object *pare= ntobj, =20 out: /* - * We want @obj's reference to be 1 on success, 0 on failure. - * On success, it's 2: one taken by object_initialize(), and one - * by object_property_add_child(). - * On failure in object_initialize() or earlier, it's 1. - * On failure afterwards, it's also 1: object_unparent() releases + * We want @obj's reference to be 0 on success, UINT32_MAX on failure. + * On success, it's 1: one taken by object_property_add_child(). + * On failure in object_initialize() or earlier, it's 0. + * On failure afterwards, it's also 0: object_unparent() releases * the reference taken by object_property_add_child(). */ object_unref(obj); @@ -668,7 +666,6 @@ static void object_finalize(void *data) object_property_del_all(obj); object_deinit(obj, ti); =20 - g_assert(obj->ref =3D=3D 0); g_assert(obj->parent =3D=3D NULL); if (obj->free) { obj->free(obj); @@ -1329,10 +1326,10 @@ void object_unref(void *objptr) if (!obj) { return; } - g_assert(obj->ref > 0); + g_assert(obj->ref < INT_MAX); =20 /* parent always holds a reference to its children */ - if (qatomic_fetch_dec(&obj->ref) =3D=3D 1) { + if (qatomic_fetch_dec(&obj->ref) =3D=3D 0) { object_finalize(obj); } } diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplis= t.c index 89de92b7d91f..b5527dd52afd 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -351,7 +351,7 @@ static void test_dummy_createv_tree(void) NULL)); =20 g_assert(err =3D=3D NULL); - g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 1); + g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 0); g_assert_cmpstr(dobj->sv, =3D=3D, "Hiss hiss hiss"); g_assert(dobj->bv =3D=3D true); g_assert(dobj->av =3D=3D DUMMY_PLATYPUS); @@ -375,7 +375,7 @@ static void test_dummy_createv_parentless(void) NULL)); =20 g_assert(err =3D=3D NULL); - g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 1); + g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 0); g_assert_cmpstr(dobj->sv, =3D=3D, "Hiss hiss hiss"); g_assert(dobj->bv =3D=3D true); g_assert(dobj->av =3D=3D DUMMY_PLATYPUS); @@ -414,7 +414,7 @@ static void test_dummy_createlist_tree(void) NULL)); =20 g_assert(err =3D=3D NULL); - g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 1); + g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 0); g_assert_cmpstr(dobj->sv, =3D=3D, "Hiss hiss hiss"); g_assert(dobj->bv =3D=3D true); g_assert(dobj->av =3D=3D DUMMY_PLATYPUS); @@ -450,7 +450,7 @@ static void test_dummy_createlist_parentless(void) NULL)); =20 g_assert(err =3D=3D NULL); - g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 1); + g_assert_cmpint(dobj->parent_obj.ref, =3D=3D, 0); g_assert_cmpstr(dobj->sv, =3D=3D, "Hiss hiss hiss"); g_assert(dobj->bv =3D=3D true); g_assert(dobj->av =3D=3D DUMMY_PLATYPUS); --=20 2.54.0 From nobody Sun Jul 26 13:30:35 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=reject dis=none) header.from=rsg.ci.i.u-tokyo.ac.jp Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17814968379451019.2089473216199; Sun, 14 Jun 2026 21:13:57 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wYyhU-0000kw-Vh; Mon, 15 Jun 2026 00:13:29 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wYygl-0000dK-PN; Mon, 15 Jun 2026 00:12:47 -0400 Received: from www3579.sakura.ne.jp ([49.212.243.89]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wYygf-0002W0-Mk; Mon, 15 Jun 2026 00:12:42 -0400 Received: from h183.csg.ci.i.u-tokyo.ac.jp (h183.csg.ci.i.u-tokyo.ac.jp [133.11.54.183]) (authenticated bits=0) by www3579.sakura.ne.jp (8.16.1/8.16.1) with ESMTPSA id 65F4CEdg067027 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Mon, 15 Jun 2026 13:12:19 +0900 (JST) (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp) DKIM-Signature: a=rsa-sha256; bh=YyAGKnXvH/LGzt6y5HcFEyLsMb6x8XNx72eLD1BWzf8=; c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp; h=From:Message-Id:To:Subject:Date; s=rs20250326; t=1781496739; v=1; b=N6xf4pkcFACzXMrl5cvBFnX1t1kV7aMiSLLbdib6BF8Yij1b6e8T+8czgAmKX3/8 qtvkMvVjPqJ5kKB4wj5PPFZfQwMAU6r2NAzYdlzq/LdzsPKYHgBtus37dr0aCIwl KYZW5Fq+9pMeA+4aT3p+LIAGu6VSgwwhSqB04EITMQbD3/HT3FjncszFtUBv0+Os SPvDf7lJrslf98PTMFwH2qcKAvrJv9UNS7X34vLCDjU9hQ6BXmNAXyeFC+l6MgKe sE00YyiJHEaZThy2xhF4bguGMGdAUMG6gjWoEOWbr9Cr7iDNiJ6ZyqxGqf/3YrUC UvnsXCUJKEWtjsXKBlU7bg== From: Akihiko Odaki Date: Mon, 15 Jun 2026 13:11:07 +0900 Subject: [PATCH 2/2] qom: Manage references to embedded child objects MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260615-embedded-v1-2-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp> References: <20260615-embedded-v1-0-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp> In-Reply-To: <20260615-embedded-v1-0-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp> To: qemu-devel@nongnu.org Cc: Paolo Bonzini , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Manos Pitsidianakis , qemu-rust@nongnu.org, Peter Xu , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Alberto Garcia , Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org, Akihiko Odaki X-Mailer: b4 0.16-dev-16047 X-Developer-Signature: v=1; a=openpgp-sha256; l=11387; i=odaki@rsg.ci.i.u-tokyo.ac.jp; h=from:subject:message-id; bh=NiwcA/iX1K8nXPU3LxiIUkcDgIjjHRx062jRLUc2jD4=; b=owGbwMvMwCWmMbc20y1CyJDxtFoSQ5Z+9aLmKOtoG6U7nSe+5qtus9rmdFlWsfDtvlINi71W1 y4FvSvpKGVhEONikBVTZEkp2s2tEV37qTAhvgVmDisTyBAGLk4BmEjjKYZ/Cr4MXcLpD0QUJGvC snr272IVYs57sUhzabqd/I72hQ+fMjL8W/Z00jahTWs+zpeJ3DaXn6cyLaXgc9Lihkm8zo49SXM ZAQ== X-Developer-Key: i=odaki@rsg.ci.i.u-tokyo.ac.jp; a=openpgp; fpr=AEDC03C9AF734F2EC26A7BFFA4BAEAA73536753C Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=49.212.243.89; envelope-from=odaki@rsg.ci.i.u-tokyo.ac.jp; helo=www3579.sakura.ne.jp X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1781496842064158500 Problem =3D=3D=3D=3D=3D=3D=3D The Rust wrapper for object_ref() is marked unsafe and has: > The object must not be embedded in another unless the outer > object is guaranteed to have a longer lifetime. In other words, object_ref() does not work for embedded objects and does not keep embedded objects alive. MemoryRegion has its own memory_region_ref() helper to call object_ref() on its owner for this reason. However, this is insufficient to avoid calling object_ref() for all embedded objects. For example, consider an embedded Device that has a MemoryRegion. When referencing a MemoryRegion for guest memory access, QEMU automatically references the owning Device to keep the MemoryRegion alive. However, that reference is ineffective if the Device itself is embedded, because object_ref() does not keep the containing storage alive. One concrete case is qemu-xhci: XHCIPciState embeds XHCIState as a child object, and the embedded XHCIState owns the MemoryRegion used for PCI BAR 0. When memory core references that region, memory_region_ref() references the embedded XHCIState owner, but that does not keep the containing XHCIPciState storage alive across runtime unplug. To avoid such a use-after-free hazard, memory_region_init*() would also need a SAFETY comment saying "the owner must not be embedded in another unless its outer object is guaranteed to have a longer lifetime." The unsafe nature of object_ref() propagates across the codebase and spreads SAFETY comments across the codebase, and any failure to fulfill the requirement can cause use-after-free. Solution =3D=3D=3D=3D=3D=3D=3D=3D Eliminate this whole class of use-after-free hazard by properly managing references to the "storage" of embedded child objects. Objects now have a separate storage reference counter for their storage. If an object is embedded, the reference counter is set to UINT32_MAX. Otherwise, it represents the number of references to the object's storage - 1. Object::free is now a union with Object::storage, which holds a reference to the storage for an embedded object. This forms the following reference graph: Parent -> Storage Parent -> Embedded Child -> Storage Functions for taking and dropping references to object storage are also exposed as APIs. This is particularly useful when the storage needs to be freed asynchronously due to RCU, for example. A possible concern is that this adds a uint32_t to Object. This is not a problem in most situations where the host uses 64-bit addressing because the member is added to a gap needed for alignment. It does increase memory usage for 32-bit hosts, but proliferation of SAFETY comments to avoid the overhead is unlikely to be worthwhile. Note that the Rust wrapper for object_ref() is still marked as unsafe. This is because an object implemented in C may have pointers whose lifetimes are not properly extended according to the reference counter. Signed-off-by: Akihiko Odaki --- include/qom/object.h | 39 +++++++++++++++++++++++++++------- qom/object.c | 60 +++++++++++++++++++++++++++++++++++++++++++++---= ---- rust/qom/src/qom.rs | 8 +++---- 3 files changed, 87 insertions(+), 20 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index c4dcf65dcf7f..b5d656e3758c 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -155,8 +155,12 @@ struct Object { /* private: */ ObjectClass *class; - ObjectFree *free; + union { + ObjectFree *free; + Object *storage; + }; GHashTable *properties; + uint32_t storage_ref; uint32_t ref; Object *parent; }; @@ -613,7 +617,7 @@ struct InterfaceClass * @klass: The class to instantiate. * * This function will initialize a new object using heap allocated memory. - * The returned object has a reference count of 1, and will be freed when + * The returned object has a reference count of 1, and will be finalized w= hen * the last reference is dropped. * * Returns: The newly allocated and instantiated object. @@ -625,7 +629,7 @@ Object *object_new_with_class(ObjectClass *klass); * @typename: The name of the type of the object to instantiate. * * This function will initialize a new object using heap allocated memory. - * The returned object has a reference count of 1, and will be freed when + * The returned object has a reference count of 1, and will be finalized w= hen * the last reference is dropped. * * Returns: The newly allocated and instantiated object. @@ -641,7 +645,7 @@ Object *object_new(const char *typename); * @...: list of property names and values * * This function will initialize a new object using heap allocated memory. - * The returned object has a reference count of 1, and will be freed when + * The returned object has a reference count of 1, and will be finalized w= hen * the last reference is dropped. * * The @id parameter will be used when registering the object as a @@ -1120,8 +1124,8 @@ GSList *object_class_get_list_sorted(const char *impl= ements_type, * object_ref: * @obj: the object * - * Increase the reference count of a object. A object cannot be freed as = long - * as a reference remains. + * Increase the reference count of a object. A object cannot be finalized= as + * long as a reference remains. * Returns: @obj */ Object *object_ref(void *obj); @@ -1130,11 +1134,30 @@ Object *object_ref(void *obj); * object_unref: * @obj: the object * - * Decrease the reference count of a object. A object cannot be freed as = long - * as a reference remains. + * Decrease the reference count of a object. A object cannot be finalized= as + * long as a reference remains. */ void object_unref(void *obj); =20 +/** + * object_storage_ref: + * @obj: the object + * + * Increase the reference count of a object's storage. A object cannot be + * freed as long as its storage is referenced. + * Returns: the object that provides the storage + */ +Object *object_storage_ref(void *obj); + +/** + * object_storage_unref: + * @obj: the object + * + * Decrease the reference count of a object's storage. A object cannot be + * freed as long as its storage is referenced. + */ +void object_storage_unref(void *obj); + /** * object_property_try_add: * @obj: the object to add a property to diff --git a/qom/object.c b/qom/object.c index 761eff1337e6..0a2c7ce9710d 100644 --- a/qom/object.c +++ b/qom/object.c @@ -487,7 +487,8 @@ static void object_class_property_init_all(Object *obj) } } =20 -static void object_initialize_with_type(Object *obj, size_t size, TypeImpl= *type) +static void object_initialize_with_type(Object *obj, size_t size, + TypeImpl *type, Object *storage) { type_initialize(type); =20 @@ -496,6 +497,10 @@ static void object_initialize_with_type(Object *obj, s= ize_t size, TypeImpl *type g_assert(size >=3D type->instance_size); =20 memset(obj, 0, type->instance_size); + if (storage) { + obj->storage_ref =3D UINT32_MAX; + obj->storage =3D object_storage_ref(storage); + } obj->class =3D type->class; object_class_property_init_all(obj); obj->properties =3D g_hash_table_new_full(g_str_hash, g_str_equal, @@ -508,7 +513,7 @@ void object_initialize(void *data, size_t size, const c= har *typename) { TypeImpl *type =3D type_get_or_load_by_name(typename, &error_fatal); =20 - object_initialize_with_type(data, size, type); + object_initialize_with_type(data, size, type, NULL); } =20 bool object_initialize_child_with_props(Object *parentobj, @@ -531,14 +536,15 @@ bool object_initialize_child_with_props(Object *paren= tobj, bool object_initialize_child_with_propsv(Object *parentobj, const char *propname, void *childobj, size_t size, - const char *type, + const char *typename, Error **errp, va_list vargs) { + TypeImpl *type =3D type_get_or_load_by_name(typename, &error_fatal); bool ok =3D false; Object *obj; UserCreatable *uc; =20 - object_initialize(childobj, size, type); + object_initialize_with_type(childobj, size, type, parentobj); obj =3D OBJECT(childobj); =20 if (!object_set_propv(obj, vargs, errp)) { @@ -667,9 +673,7 @@ static void object_finalize(void *data) object_deinit(obj, ti); =20 g_assert(obj->parent =3D=3D NULL); - if (obj->free) { - obj->free(obj); - } + object_storage_unref(obj); } =20 /* Find the minimum alignment guaranteed by the system malloc. */ @@ -708,7 +712,7 @@ static Object *object_new_with_type(Type type) obj_free =3D qemu_vfree; } =20 - object_initialize_with_type(obj, size, type); + object_initialize_with_type(obj, size, type, NULL); obj->free =3D obj_free; =20 trace_object_new(obj, obj->class->type->name); @@ -1334,6 +1338,46 @@ void object_unref(void *objptr) } } =20 +Object *object_storage_ref(void *objptr) +{ + Object *obj =3D OBJECT(objptr); + uint32_t ref; + + if (!obj) { + return NULL; + } + + while (qatomic_read(&obj->storage_ref) =3D=3D UINT32_MAX) { + obj =3D obj->storage; + } + + ref =3D qatomic_fetch_inc(&obj->storage_ref); + /* Assert waaay before the integer overflows */ + g_assert(ref < INT_MAX); + return obj; +} + +void object_storage_unref(void *objptr) +{ + Object *obj =3D OBJECT(objptr); + uint32_t ref; + + if (!obj) { + return; + } + + while (qatomic_read(&obj->storage_ref) =3D=3D UINT32_MAX) { + obj =3D obj->storage; + } + + ref =3D qatomic_fetch_dec(&obj->storage_ref); + g_assert(ref < INT_MAX); + + if (ref =3D=3D 0 && obj->free) { + obj->free(obj); + } +} + ObjectProperty * object_property_try_add(Object *obj, const char *name, const char *type, ObjectPropertyAccessor *get, diff --git a/rust/qom/src/qom.rs b/rust/qom/src/qom.rs index cc00ddcfc988..34f7fff64083 100644 --- a/rust/qom/src/qom.rs +++ b/rust/qom/src/qom.rs @@ -809,8 +809,8 @@ impl Owned { /// # Safety /// /// The caller must indeed own a reference to the QOM object. - /// The object must not be embedded in another unless the outer - /// object is guaranteed to have a longer lifetime. + /// The object's implementation must guarantee functionality as long a= s it + /// is referenced. /// /// A raw pointer obtained via [`Owned::into_raw()`] can always be pas= sed /// back to `from_raw()` (assuming the original `Owned` was valid!), @@ -836,8 +836,8 @@ pub fn into_raw(src: Owned) -> *mut T { /// /// # Safety /// - /// The object must not be embedded in another, unless the outer - /// object is guaranteed to have a longer lifetime. + /// The object's implementation must guarantee functionality as long a= s it + /// is referenced. pub unsafe fn from(obj: &T) -> Self { unsafe { object_ref(obj.as_object_mut_ptr().cast::()); --=20 2.54.0