With code like
Object *obj = object_new(TYPE_BLAH)
the caller can be pretty confident that they will successfully create
an object instance of TYPE_BLAH. They know exactly what type has been
requested, so it passing an abstract type for example, it is a clear
programmer error that they'll get an assertion failure.
Conversely with code like
void somefunc(const char *typename) {
Object * obj = object_new(typename)
...
}
all bets are off, because the call of object_new() knows nothing
about what 'typename' resolves to. It could easily be an abstract
type. As a result, many code paths have added a manual check ahead
of time
if (object_class_is_abstract(typename)) {
error_setg(errp, ....)
}
...except for where we forget to do this, such as qdev_new().
Overall 'object_new' is a bad design because it is inherantly
unsafe to call with unvalidated typenames.
This problem is made worse by the proposal to introduce the idea
of 'singleton' classes[1].
Thus, this series suggests a way to improve safety at build
time. The core idea is to allow 'object_new' to continue to be
used *if-and-only-if* given a static, const string, because that
scenario indicates the caller is aware of what type they are
creating at build time.
A new 'object_new_dynamic' method is proposed for cases where
the typename is dynamically chosen at runtime. This method has
an "Error **errp" parameter, which can report when an abstract
type is created, leaving the assert()s only for scenarios which
are unambiguous programmer errors.
With a little macro magic, we guarantee a compile error is
generated if 'object_new' is called with a dynamic type, forcing
all potentially unsafe code over to object_new_dynamic.
This is more tractable than adding 'Error **errp' to 'object_new'
as only a handful of places use a dynamic type name.
NB, this is an RFC as it is not fully complete.
* I have only converted enough object_new -> object_new_dynamic
to make the x86_64-softmu target compile. It probably fails on
other targets.
* I have not run any test suites yet, so they may or may not pass
* I stubbed qdev_new to just pass &error_fatal. qdev_new needs
the same conceptual fix to introcce qdev_new_dynamic with
the macro magic to force its use
Obviously if there's agreement that this conceptual idea is valid,
then all these gaps would be fixed.
With this series, my objections to Peter Xu's singleton series[1]
would be largely nullified.
[1] https://lists.nongnu.org/archive/html/qemu-devel/2024-10/msg05524.html
Daniel P. Berrangé (5):
qom: refactor checking abstract property when creating instances
qom: allow failure of object_new_with_class
convert code to object_new_dynamic() where appropriate
qom: introduce object_new_dynamic()
qom: enforce use of static, const string with object_new()
accel/accel-user.c | 3 +-
chardev/char.c | 5 +++-
hw/core/bus.c | 2 +-
hw/core/cpu-common.c | 2 +-
hw/core/qdev.c | 4 +--
hw/i386/x86-common.c | 5 +++-
hw/i386/xen/xen-pvh.c | 2 +-
hw/vfio/common.c | 6 +++-
hw/vfio/container.c | 6 +++-
include/qom/object.h | 48 ++++++++++++++++++++++++++++++--
net/net.c | 10 ++++---
qom/object.c | 38 +++++++++++++++++--------
qom/object_interfaces.c | 7 ++---
qom/qom-qmp-cmds.c | 15 ++++++----
system/vl.c | 6 ++--
target/i386/cpu-apic.c | 8 +++++-
target/i386/cpu-sysemu.c | 11 ++++++--
target/i386/cpu.c | 4 +--
target/s390x/cpu_models_sysemu.c | 7 +++--
tests/unit/check-qom-interface.c | 3 +-
tests/unit/test-smp-parse.c | 20 ++++++-------
21 files changed, 151 insertions(+), 61 deletions(-)
--
2.46.0