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.