All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Cc: qemu-devel@nongnu.org, "Paolo Bonzini" <pbonzini@redhat.com>,
	"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
	qemu-rust@nongnu.org, "Peter Xu" <peterx@redhat.com>,
	"Philippe Mathieu-Daudé" <philmd@mailo.com>,
	"Alberto Garcia" <berto@igalia.com>,
	"Kevin Wolf" <kwolf@redhat.com>,
	"Hanna Reitz" <hreitz@redhat.com>,
	qemu-block@nongnu.org
Subject: Re: [PATCH 1/2] qom: Reject temporary object resurrection
Date: Mon, 15 Jun 2026 09:14:34 +0100	[thread overview]
Message-ID: <ai-0akg3wd6TRSgD@redhat.com> (raw)
In-Reply-To: <20260615-embedded-v1-1-bb0c65bf126c@rsg.ci.i.u-tokyo.ac.jp>

On Mon, Jun 15, 2026 at 01:11:06PM +0900, Akihiko Odaki wrote:
> 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.

What's the real world problem here ? 

> 
> 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.

Please no, this is horribly surprising semantics.

> 
> Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
> ---
>  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 *implements_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);
>  
> 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, Visitor *v,
>  
>  static bool throttle_group_can_be_deleted(UserCreatable *uc)
>  {
> -    return OBJECT(uc)->ref == 1;
> +    return OBJECT(uc)->ref == 0;
>  }
>  
>  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, size_t size, TypeImpl *type
>  
>      memset(obj, 0, type->instance_size);
>      obj->class = type->class;
> -    object_ref(obj);
>      object_class_property_init_all(obj);
>      obj->properties = 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 *parentobj,
>  
>  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);
>  
> -    g_assert(obj->ref == 0);
>      g_assert(obj->parent == 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);
>  
>      /* parent always holds a reference to its children */
> -    if (qatomic_fetch_dec(&obj->ref) == 1) {
> +    if (qatomic_fetch_dec(&obj->ref) == 0) {
>          object_finalize(obj);
>      }
>  }
> diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.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));
>  
>      g_assert(err == NULL);
> -    g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
> +    g_assert_cmpint(dobj->parent_obj.ref, ==, 0);
>      g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>      g_assert(dobj->bv == true);
>      g_assert(dobj->av == DUMMY_PLATYPUS);
> @@ -375,7 +375,7 @@ static void test_dummy_createv_parentless(void)
>                                           NULL));
>  
>      g_assert(err == NULL);
> -    g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
> +    g_assert_cmpint(dobj->parent_obj.ref, ==, 0);
>      g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>      g_assert(dobj->bv == true);
>      g_assert(dobj->av == DUMMY_PLATYPUS);
> @@ -414,7 +414,7 @@ static void test_dummy_createlist_tree(void)
>                          NULL));
>  
>      g_assert(err == NULL);
> -    g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
> +    g_assert_cmpint(dobj->parent_obj.ref, ==, 0);
>      g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>      g_assert(dobj->bv == true);
>      g_assert(dobj->av == DUMMY_PLATYPUS);
> @@ -450,7 +450,7 @@ static void test_dummy_createlist_parentless(void)
>                                NULL));
>  
>      g_assert(err == NULL);
> -    g_assert_cmpint(dobj->parent_obj.ref, ==, 1);
> +    g_assert_cmpint(dobj->parent_obj.ref, ==, 0);
>      g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>      g_assert(dobj->bv == true);
>      g_assert(dobj->av == DUMMY_PLATYPUS);
> 
> -- 
> 2.54.0
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



  reply	other threads:[~2026-06-15  8:15 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-15  4:11 [PATCH 0/2] qom: Tighten object lifetime handling Akihiko Odaki
2026-06-15  4:11 ` [PATCH 1/2] qom: Reject temporary object resurrection Akihiko Odaki
2026-06-15  8:14   ` Daniel P. Berrangé [this message]
2026-06-15  8:34   ` Kevin Wolf
2026-06-15  8:50     ` Akihiko Odaki
2026-06-15  4:11 ` [PATCH 2/2] qom: Manage references to embedded child objects Akihiko Odaki
2026-06-15  8:12   ` Daniel P. Berrangé
2026-06-15  8:31     ` Peter Maydell
2026-06-15  8:34       ` Daniel P. Berrangé
2026-06-15  9:28         ` Peter Maydell
2026-06-15 13:42           ` Mark Cave-Ayland
2026-06-15 13:55             ` Akihiko Odaki
2026-06-15 14:01             ` Daniel P. Berrangé
2026-06-15 20:09             ` BALATON Zoltan
2026-06-15 13:29     ` Mark Cave-Ayland
2026-06-15 10:59   ` Daniel P. Berrangé
2026-06-15 12:02     ` Akihiko Odaki

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=ai-0akg3wd6TRSgD@redhat.com \
    --to=berrange@redhat.com \
    --cc=berto@igalia.com \
    --cc=hreitz@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=odaki@rsg.ci.i.u-tokyo.ac.jp \
    --cc=pbonzini@redhat.com \
    --cc=peterx@redhat.com \
    --cc=philmd@mailo.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-rust@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.