Commit 8dc8f7b5bc for qemu.org

commit 8dc8f7b5bc755227c72eb74aa332e7a6bbb90066
Author: Markus Armbruster <armbru@redhat.com>
Date:   Fri Jan 23 09:59:24 2026 +0100

    qdev-monitor: Fix qdev ID validation regression

    User-created qdevs with ID show up at /machine/peripheral/ID.

    When we restricted QemOpts IDs to letters, digits, '-', '.', '_',
    starting with a letter in commit b560a9ab9be: (qemu-option: Reject
    anti-social IDs) a long time ago, this also covered qdev IDs.  Looks
    like this:

        (qemu) device_add usb-mouse,id=/
        qemu-system-x86_64: Parameter 'id' expects an identifier
        Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
        Try "help device_add" for more information

    QMP, however:

        {"execute": "device_add", "arguments": {"driver": "usb-mouse", "id": "/"}}
        {"return": {}}

    This creates a device with canonical path "/machine/peripheral//".
    That way is madness.

    We accidentally bypassed qdev ID validation for QMP when we cut the
    detour through QemuOpts in commit b30d8054642.

    Fix by validating IDs one layer down, in qdev_set_id().

    Arguably, QOM should protect itself from QOM path components
    containing '/', but let's just fix the regression for now.

    Fixes: be93fd53723c (qdev-monitor: avoid QemuOpts in QMP device_add)
    Signed-off-by: Markus Armbruster <armbru@redhat.com>
    Message-ID: <20260123085924.1392134-1-armbru@redhat.com>
    Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 1ac6d9a857..5c00bbf483 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -34,6 +34,7 @@
 #include "qemu/config-file.h"
 #include "qemu/error-report.h"
 #include "qemu/help_option.h"
+#include "qemu/id.h"
 #include "qemu/option.h"
 #include "qemu/qemu-print.h"
 #include "qemu/option_int.h"
@@ -601,14 +602,17 @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
      * has no parent
      */
     if (id) {
+        if (!id_wellformed(id)) {
+            error_setg(errp, "Invalid qdev ID '%s'", id);
+            goto err;
+        }
         prop = object_property_try_add_child(qdev_get_peripheral(), id,
                                              OBJECT(dev), NULL);
         if (prop) {
             dev->id = id;
         } else {
             error_setg(errp, "Duplicate device ID '%s'", id);
-            g_free(id);
-            return NULL;
+            goto err;
         }
     } else {
         static int anon_count;
@@ -619,6 +623,10 @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
     }

     return prop->name;
+
+err:
+    g_free(id);
+    return NULL;
 }

 BusState *qdev_find_default_bus(DeviceClass *dc, Error **errp)