Skip to content

Commit 4c315c2

Browse files
author
Markus Armbruster
committed
qdev: Protect device-list-properties against broken devices
Several devices don't survive object_unref(object_new(T)): they crash or hang during cleanup, or they leave dangling pointers behind. This breaks at least device-list-properties, because qmp_device_list_properties() needs to create a device to find its properties. Broken in commit f4eb32b "qmp: show QOM properties in device-list-properties", v2.1. Example reproducer: $ qemu-system-aarch64 -nodefaults -display none -machine none -S -qmp stdio {"QMP": {"version": {"qemu": {"micro": 50, "minor": 4, "major": 2}, "package": ""}, "capabilities": []}} { "execute": "qmp_capabilities" } {"return": {}} { "execute": "device-list-properties", "arguments": { "typename": "pxa2xx-pcmcia" } } qemu-system-aarch64: /home/armbru/work/qemu/memory.c:1307: memory_region_finalize: Assertion `((&mr->subregions)->tqh_first == ((void *)0))' failed. Aborted (core dumped) [Exit 134 (SIGABRT)] Unfortunately, I can't fix the problems in these devices right now. Instead, add DeviceClass member cannot_destroy_with_object_finalize_yet to mark them: * Hang during cleanup (didn't debug, so I can't say why): "realview_pci", "versatile_pci". * Dangling pointer in cpus: most CPUs, plus "allwinner-a10", "digic", "fsl,imx25", "fsl,imx31", "xlnx,zynqmp", because they create such CPUs * Assert kvm_enabled(): "host-x86_64-cpu", host-i386-cpu", "host-powerpc64-cpu", "host-embedded-powerpc-cpu", "host-powerpc-cpu" (the powerpc ones can't currently reach the assertion, because the CPUs are only registered when KVM is enabled, but the assertion is arguably in the wrong place all the same) Make qmp_device_list_properties() fail cleanly when the device is so marked. This improves device-list-properties from "crashes, hangs or leaves dangling pointers behind" to "fails". Not a complete fix, just a better-than-nothing work-around. In the above reproducer, device-list-properties now fails with "Can't list properties of device 'pxa2xx-pcmcia'". This also protects -device FOO,help, which uses the same machinery since commit ef52358 "qdev-monitor: include QOM properties in -device FOO, help output", v2.2. Example reproducer: $ qemu-system-aarch64 -machine none -device pxa2xx-pcmcia,help Before: qemu-system-aarch64: .../memory.c:1307: memory_region_finalize: Assertion `((&mr->subregions)->tqh_first == ((void *)0))' failed. After: Can't list properties of device 'pxa2xx-pcmcia' Cc: "Andreas Färber" <[email protected]> Cc: "Edgar E. Iglesias" <[email protected]> Cc: Alexander Graf <[email protected]> Cc: Anthony Green <[email protected]> Cc: Aurelien Jarno <[email protected]> Cc: Bastian Koppelmann <[email protected]> Cc: Blue Swirl <[email protected]> Cc: Eduardo Habkost <[email protected]> Cc: Guan Xuetao <[email protected]> Cc: Jia Liu <[email protected]> Cc: Leon Alrae <[email protected]> Cc: Mark Cave-Ayland <[email protected]> Cc: Max Filippov <[email protected]> Cc: Michael Walle <[email protected]> Cc: Paolo Bonzini <[email protected]> Cc: Peter Maydell <[email protected]> Cc: Richard Henderson <[email protected]> Cc: [email protected] Cc: [email protected] Signed-off-by: Markus Armbruster <[email protected]> Reviewed-by: Eduardo Habkost <[email protected]> Message-Id: <[email protected]>
1 parent edb1523 commit 4c315c2

File tree

27 files changed

+185
-27
lines changed

27 files changed

+185
-27
lines changed

hw/arm/allwinner-a10.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ static void aw_a10_class_init(ObjectClass *oc, void *data)
103103
DeviceClass *dc = DEVICE_CLASS(oc);
104104

105105
dc->realize = aw_a10_realize;
106+
107+
/*
108+
* Reason: creates an ARM CPU, thus use after free(), see
109+
* arm_cpu_class_init()
110+
*/
111+
dc->cannot_destroy_with_object_finalize_yet = true;
106112
}
107113

108114
static const TypeInfo aw_a10_type_info = {

hw/arm/digic.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ static void digic_class_init(ObjectClass *oc, void *data)
9797
DeviceClass *dc = DEVICE_CLASS(oc);
9898

9999
dc->realize = digic_realize;
100+
101+
/*
102+
* Reason: creates an ARM CPU, thus use after free(), see
103+
* arm_cpu_class_init()
104+
*/
105+
dc->cannot_destroy_with_object_finalize_yet = true;
100106
}
101107

102108
static const TypeInfo digic_type_info = {

hw/arm/fsl-imx25.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
284284
DeviceClass *dc = DEVICE_CLASS(oc);
285285

286286
dc->realize = fsl_imx25_realize;
287+
288+
/*
289+
* Reason: creates an ARM CPU, thus use after free(), see
290+
* arm_cpu_class_init()
291+
*/
292+
dc->cannot_destroy_with_object_finalize_yet = true;
287293
}
288294

289295
static const TypeInfo fsl_imx25_type_info = {

hw/arm/fsl-imx31.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
258258
DeviceClass *dc = DEVICE_CLASS(oc);
259259

260260
dc->realize = fsl_imx31_realize;
261+
262+
/*
263+
* Reason: creates an ARM CPU, thus use after free(), see
264+
* arm_cpu_class_init()
265+
*/
266+
dc->cannot_destroy_with_object_finalize_yet = true;
261267
}
262268

263269
static const TypeInfo fsl_imx31_type_info = {

hw/arm/xlnx-zynqmp.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
271271

272272
dc->props = xlnx_zynqmp_props;
273273
dc->realize = xlnx_zynqmp_realize;
274+
275+
/*
276+
* Reason: creates an ARM CPU, thus use after free(), see
277+
* arm_cpu_class_init()
278+
*/
279+
dc->cannot_destroy_with_object_finalize_yet = true;
274280
}
275281

276282
static const TypeInfo xlnx_zynqmp_type_info = {

hw/pci-host/versatile.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data)
500500
dc->reset = pci_vpb_reset;
501501
dc->vmsd = &pci_vpb_vmstate;
502502
dc->props = pci_vpb_properties;
503+
/* Reason: object_unref() hangs */
504+
dc->cannot_destroy_with_object_finalize_yet = true;
503505
}
504506

505507
static const TypeInfo pci_vpb_info = {
@@ -521,10 +523,19 @@ static void pci_realview_init(Object *obj)
521523
s->mem_win_size[2] = 0x08000000;
522524
}
523525

526+
static void pci_realview_class_init(ObjectClass *class, void *data)
527+
{
528+
DeviceClass *dc = DEVICE_CLASS(class);
529+
530+
/* Reason: object_unref() hangs */
531+
dc->cannot_destroy_with_object_finalize_yet = true;
532+
}
533+
524534
static const TypeInfo pci_realview_info = {
525535
.name = "realview_pci",
526536
.parent = TYPE_VERSATILE_PCI,
527537
.instance_init = pci_realview_init,
538+
.class_init = pci_realview_class_init,
528539
};
529540

530541
static void versatile_pci_register_types(void)

include/hw/qdev-core.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,19 @@ typedef struct DeviceClass {
114114
* TODO remove once we're there
115115
*/
116116
bool cannot_instantiate_with_device_add_yet;
117+
/*
118+
* Does this device model survive object_unref(object_new(TNAME))?
119+
* All device models should, and this flag shouldn't exist. Some
120+
* devices crash in object_new(), some crash or hang in
121+
* object_unref(). Makes introspecting properties with
122+
* qmp_device_list_properties() dangerous. Bad, because it's used
123+
* by -device FOO,help. This flag serves to protect that code.
124+
* It should never be set without a comment explaining why it is
125+
* set.
126+
* TODO remove once we're there
127+
*/
128+
bool cannot_destroy_with_object_finalize_yet;
129+
117130
bool hotpluggable;
118131

119132
/* callbacks */

qmp.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,11 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
521521
return NULL;
522522
}
523523

524+
if (DEVICE_CLASS(klass)->cannot_destroy_with_object_finalize_yet) {
525+
error_setg(errp, "Can't list properties of device '%s'", typename);
526+
return NULL;
527+
}
528+
524529
obj = object_new(typename);
525530

526531
QTAILQ_FOREACH(prop, &obj->properties, node) {

target-alpha/cpu.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data)
298298
dc->vmsd = &vmstate_alpha_cpu;
299299
#endif
300300
cc->gdb_num_core_regs = 67;
301+
302+
/*
303+
* Reason: alpha_cpu_initfn() calls cpu_exec_init(), which saves
304+
* the object in cpus -> dangling pointer after final
305+
* object_unref().
306+
*/
307+
dc->cannot_destroy_with_object_finalize_yet = true;
301308
}
302309

303310
static const TypeInfo alpha_cpu_type_info = {

target-arm/cpu.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,17 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
14271427
cc->debug_excp_handler = arm_debug_excp_handler;
14281428

14291429
cc->disas_set_info = arm_disas_set_info;
1430+
1431+
/*
1432+
* Reason: arm_cpu_initfn() calls cpu_exec_init(), which saves
1433+
* the object in cpus -> dangling pointer after final
1434+
* object_unref().
1435+
*
1436+
* Once this is fixed, the devices that create ARM CPUs should be
1437+
* updated not to set cannot_destroy_with_object_finalize_yet,
1438+
* unless they still screw up something else.
1439+
*/
1440+
dc->cannot_destroy_with_object_finalize_yet = true;
14301441
}
14311442

14321443
static void cpu_register(const ARMCPUInfo *info)

0 commit comments

Comments
 (0)