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.
> + */
> +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 ;
> }
>
> 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?
> + }
> }
>
> 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
>