On 17/09/2026 09:52, Marc-André Lureau wrote:

Hi

On Wed, Sep 2, 2026 at 6:42 PM Mark Cave-Ayland
<[email protected]> wrote:

This is eventually intended to be a replacement for object_property_add_alias()
which uses an object property instead of a class property.

With the advent of class properties, it is possible that QOM may attempt to
set or retrieve the value of an unset alias property. Update the existing
property_get_alias() and property_set_alias() functions to use the null visitor
if the alias target has not been set, and property_resolve_alias() to return
NULL for the same case.

This ensures that unset class alias properties accessed e.g. via the monitor do
not cause QEMU to crash.

Signed-off-by: Mark Cave-Ayland <[email protected]>
---
  include/qom/object.h |  25 +++++++++++
  qom/object.c         | 100 +++++++++++++++++++++++++++++++++++++------
  2 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/include/qom/object.h b/include/qom/object.h
index e24f0a2b2d..96c76975ef 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -2263,6 +2263,11 @@ ObjectProperty 
*object_class_static_property_add_uint64_ptr(ObjectClass *klass,
                                            const uint64_t *v,
                                            ObjectPropertyFlags flags);

+typedef enum {
+    /* private */
+    OBJ_PROP_ALIAS_CLASS = 0x1,
+} ObjectPropertyAliasFlags;
+
  /**
   * object_property_add_alias:
   * @obj: the object to add a property to
@@ -2283,6 +2288,26 @@ ObjectProperty 
*object_class_static_property_add_uint64_ptr(ObjectClass *klass,
  ObjectProperty *object_property_add_alias(Object *obj, const char *name,
                                 Object *target_obj, const char *target_name);

+/**
+ * object_class_property_add_alias:
+ * @klass: the object class to add a property to
+ * @name: the name of the property
+ * @offset: the offset from the object instance where the object alias is
+ *   stored
+ * @target_type: QOM type we expect the alias to resolve to
+ * @target_name: the name of the property on the forwarded object
+ *
+ * Add an alias for a property on an object.  This function will add a property
+ * of the same type as the forwarded property.
+ *
+ * Returns: The newly added property on success, or %NULL on failure.

actually, it assert() on error and never returns NULL.

I was able to reproduce this by attempting to add a class alias property with the same name again, and then started to look at other related functions do to understand what the behaviour should be. There seems to be a number of existing issues here:

- object_property_try_add() returns NULL in the case a property cannot
  be created (which seems to be only when an attempt to make add a
  property that already exists). However since object_property_add()
  always passes &error_abort, then QEMU terminates immediately without
  returning the NULL.

- object_class_property_add() will currently assert() if an attempt is
  made to add a duplicate property: it is fairly trivial to update it to
  return the same error that object_property_add() does, however it is
  interesting to note that the kernel-doc states that
  error_setg(&error_abort, ...) should NOT be used. However in this case
  it appears to return a genuinely helpful message which I think is
  worth keeping.

- There are several places in qom/object.c that do not check the return
  value of object_property_add() / object_class_property_add() such as
  object_add_link_prop(), object_property_add_alias() which could
  potentially dereference a NULL pointer.

It feels like the assumption is actually that these functions will &error_abort rather than returning NULL as indicated by the kernel-doc. Any thoughts on what we should do here? Ideally we want the same behaviour between the object_property*() and object_class_property*() functions to aid the transition.
+ */
+ObjectProperty *
+object_class_property_add_alias(ObjectClass *klass, const char *name,
+                                ptrdiff_t offset,
+                                const char *target_type,
+                                const char *target_name);
+
  /**
   * object_property_add_const_link:
   * @obj: the object to add a property to
diff --git a/qom/object.c b/qom/object.c
index a904f53ed6..9a05e189ea 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -34,6 +34,7 @@
  #include "qom/qom-qobject.h"
  #include "qobject/qbool.h"
  #include "qobject/qlist.h"
+#include "qobject/qnull.h"
  #include "qobject/qnum.h"
  #include "qobject/qstring.h"
  #include "qemu/error-report.h"
@@ -3039,14 +3040,22 @@ object_class_static_property_add_uint64_ptr(ObjectClass 
*klass,
  }

  typedef struct {
-    Object *target_obj;
+    union {
+        Object *target_obj; /* if !OBJ_PROP_ALIAS_CLASS */
+        ptrdiff_t offset;   /* if OBJ_PROP_ALIAS_CLASS */
+    };
      char *target_name;
+    ObjectPropertyAliasFlags flags;
  } AliasProperty;

  static Object **
-object_alias_get_targetp(Object *obj, AliasProperty *lprop)
+object_alias_get_targetp(Object *obj, AliasProperty *aprop)
  {
-    return &lprop->target_obj;
+    if (aprop->flags & OBJ_PROP_ALIAS_CLASS) {
+        return (void *)obj + aprop->offset;
+    } else {
+        return &aprop->target_obj;
+    }
  }

  static void property_get_alias(Object *obj, Visitor *v, const char *name,
@@ -3054,10 +3063,18 @@ static void property_get_alias(Object *obj, Visitor *v, 
const char *name,
  {
      AliasProperty *prop = opaque;
      Object **target_obj = object_alias_get_targetp(obj, prop);
-    Visitor *alias_v = visitor_forward_field(v, prop->target_name, name);

-    object_property_get(*target_obj, prop->target_name, alias_v, errp);
-    visit_free(alias_v);
+    if (*target_obj) {
+        Visitor *alias_v = visitor_forward_field(v, prop->target_name, name);
+
+        object_property_get(*target_obj, prop->target_name, alias_v, errp);
+        visit_free(alias_v);
+    } else {
+        QNull *null = NULL;
+
+        visit_type_null(v, NULL, &null, errp);
+        qnull_unref(null);
+    };

extra ;

Ooops, thanks.

  }

  static void property_set_alias(Object *obj, Visitor *v, const char *name,
@@ -3065,10 +3082,18 @@ static void property_set_alias(Object *obj, Visitor *v, 
const char *name,
  {
      AliasProperty *prop = opaque;
      Object **target_obj = object_alias_get_targetp(obj, prop);
-    Visitor *alias_v = visitor_forward_field(v, prop->target_name, name);

-    object_property_set(*target_obj, prop->target_name, alias_v, errp);
-    visit_free(alias_v);
+    if (*target_obj) {
+        Visitor *alias_v = visitor_forward_field(v, prop->target_name, name);
+
+        object_property_set(*target_obj, prop->target_name, alias_v, errp);
+        visit_free(alias_v);
+    } else {
+        QNull *null = NULL;
+
+        visit_type_null(v, NULL, &null, errp);
+        qnull_unref(null);

Why not return an error?

I think that would make sense in the context of the discussion above.

+    }
  }

  static Object *property_resolve_alias(Object *obj, void *opaque,
@@ -3077,15 +3102,21 @@ static Object *property_resolve_alias(Object *obj, void 
*opaque,
      AliasProperty *prop = opaque;
      Object **target_obj = object_alias_get_targetp(obj, prop);

-    return object_resolve_path_component(*target_obj, prop->target_name);
+    if (*target_obj) {
+        return object_resolve_path_component(*target_obj, prop->target_name);
+    } else {
+        return NULL;
+    }
  }

  static void property_release_alias(Object *obj, const char *name, void 
*opaque)
  {
      AliasProperty *prop = opaque;

-    g_free(prop->target_name);
-    g_free(prop);
+    if (!(prop->flags & OBJ_PROP_ALIAS_CLASS)) {
+        g_free(prop->target_name);
+        g_free(prop);
+    }
  }

  ObjectProperty *
@@ -3110,6 +3141,7 @@ object_property_add_alias(Object *obj, const char *name,
      prop = g_malloc(sizeof(*prop));
      prop->target_obj = target_obj;
      prop->target_name = g_strdup(target_name);
+    prop->flags = 0;

      op = object_property_add(obj, name, prop_type,
                               property_get_alias,
@@ -3126,6 +3158,50 @@ object_property_add_alias(Object *obj, const char *name,
      return op;
  }

+ObjectProperty *
+object_class_property_add_alias(ObjectClass *klass, const char *name,
+                                ptrdiff_t offset,
+                                const char *target_type,
+                                const char *target_name)
+{
+    AliasProperty *prop;
+    ObjectProperty *op;
+    ObjectProperty *target_prop;
+    ObjectClass *target_class;
+    g_autofree char *prop_type = NULL;
+
+    target_class = object_class_by_name(target_type);
+    assert(target_class);
+    target_prop = object_class_property_find(target_class, target_name);
+    assert(target_prop);
+
+    if (object_property_is_child(target_prop)) {
+        prop_type = g_strdup_printf("link%s",
+                                    target_prop->type + strlen("child"));
+    } else {
+        prop_type = g_strdup(target_prop->type);
+    }
+
+    prop = g_malloc(sizeof(*prop));
+    prop->offset = offset;
+    prop->target_name = g_strdup(target_name);
+    prop->flags = OBJ_PROP_ALIAS_CLASS;
+
+    op = object_class_property_add(klass, name, prop_type,
+                                   property_get_alias,
+                                   property_set_alias,
+                                   property_release_alias,
+                                   prop);
+    op->resolve = property_resolve_alias;
+    if (target_prop->defval) {
+        op->defval = qobject_ref(target_prop->defval);
+    }
+
+    object_class_property_set_description(klass, op->name,
+                                          target_prop->description);
+    return op;
+}
+
  void object_property_set_description(Object *obj, const char *name,
                                       const char *description)
  {
--
2.43.0

ATB,

Mark.


Reply via email to