Gerd Hoffmann <kra...@redhat.com> writes:

> Add policies for devices which are deprecated or not secure.
> There are three options: allow, warn and deny.
>
> It's implemented for devices only.  Devices will probably be the main
> user of this.  Also object_new() can't fail as of today so it's a bit
> hard to implement policy checking at object level, especially the 'deny'
> part of it.
>
> TODO: add a command line option to actually set these policies.
>
> Comments are welcome.
>
> Signed-off-by: Gerd Hoffmann <kra...@redhat.com>
> ---
>  hw/core/qdev.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 59 insertions(+), 1 deletion(-)
>
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index f3a996f57dee..0c4e5cec743c 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -43,6 +43,15 @@
>  static bool qdev_hot_added = false;
>  bool qdev_hot_removed = false;
>  
> +enum qdev_policy {
> +    QDEV_ALLOW = 0,
> +    QDEV_WARN  = 1,
> +    QDEV_DENY  = 2,
> +};
> +
> +static enum qdev_policy qdev_deprecated_policy;
> +static enum qdev_policy qdev_not_secure_policy;
> +
>  const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
>  {
>      DeviceClass *dc = DEVICE_GET_CLASS(dev);
> @@ -144,6 +153,43 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState 
> *bus, Error **errp)
>      return true;
>  }
>  
> +static bool qdev_class_check(const char *name, ObjectClass *oc)
> +{
> +    bool allow = true;
> +
> +    if (oc->deprecated) {
> +        switch (qdev_deprecated_policy) {
> +        case QDEV_WARN:
> +            warn_report("device \"%s\" is deprecated", name);
> +            break;
> +        case QDEV_DENY:
> +            error_report("device \"%s\" is deprecated", name);
> +            allow = false;
> +            break;
> +        default:
> +            /* nothing */
> +            break;
> +        }
> +    }
> +
> +    if (oc->not_secure) {
> +        switch (qdev_not_secure_policy) {
> +        case QDEV_WARN:
> +            warn_report("device \"%s\" is not secure", name);
> +            break;
> +        case QDEV_DENY:
> +            error_report("device \"%s\" is not secure", name);
> +            allow = false;
> +            break;
> +        default:
> +            /* nothing */
> +            break;
> +        }
> +    }
> +
> +    return allow;
> +}
> +
>  DeviceState *qdev_new(const char *name)
>  {
>      ObjectClass *oc = object_class_by_name(name);
> @@ -162,14 +208,26 @@ DeviceState *qdev_new(const char *name)
>          error_report("unknown type '%s'", name);
>          abort();
>      }
> +
> +    if (!qdev_class_check(name, oc)) {

Anti-pattern: use of error_report() from within a function that returns
an error via Error **errp argument.

One such call chain: QMP core -> qmp_device_add() -> qdev_device_add()
-> qdev_device_add_from_qdict() -> qdev_new().  Your error message goes
to stderr, which is wrong.

> +        exit(1);

Worse, QMP command device_add can now kill the guest instantly.

You need to lift the check up the call chains some.

> +    }
> +
>      return DEVICE(object_new(name));
>  }
>  
>  DeviceState *qdev_try_new(const char *name)
>  {
> -    if (!module_object_class_by_name(name)) {
> +    ObjectClass *oc = module_object_class_by_name(name);
> +
> +    if (!oc) {
>          return NULL;
>      }
> +
> +    if (!qdev_class_check(name, oc)) {
> +        return NULL;
> +    }
> +
>      return DEVICE(object_new(name));
>  }


Reply via email to