mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 11:39:53 +00:00
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 commitf4eb32b
"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 commitef52358
"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" <afaerber@suse.de> Cc: "Edgar E. Iglesias" <edgar.iglesias@gmail.com> Cc: Alexander Graf <agraf@suse.de> Cc: Anthony Green <green@moxielogic.com> Cc: Aurelien Jarno <aurelien@aurel32.net> Cc: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> Cc: Blue Swirl <blauwirbel@gmail.com> Cc: Eduardo Habkost <ehabkost@redhat.com> Cc: Guan Xuetao <gxt@mprc.pku.edu.cn> Cc: Jia Liu <proljc@gmail.com> Cc: Leon Alrae <leon.alrae@imgtec.com> Cc: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Cc: Max Filippov <jcmvbkbc@gmail.com> Cc: Michael Walle <michael@walle.cc> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Peter Maydell <peter.maydell@linaro.org> Cc: Richard Henderson <rth@twiddle.net> Cc: qemu-ppc@nongnu.org Cc: qemu-stable@nongnu.org Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eduardo Habkost <ehabkost@redhat.com> Message-Id: <1443689999-12182-10-git-send-email-armbru@redhat.com>
This commit is contained in:
parent
edb1523d90
commit
4c315c2766
@ -103,6 +103,12 @@ static void aw_a10_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = aw_a10_realize;
|
||||
|
||||
/*
|
||||
* Reason: creates an ARM CPU, thus use after free(), see
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo aw_a10_type_info = {
|
||||
|
@ -97,6 +97,12 @@ static void digic_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = digic_realize;
|
||||
|
||||
/*
|
||||
* Reason: creates an ARM CPU, thus use after free(), see
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo digic_type_info = {
|
||||
|
@ -284,6 +284,12 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = fsl_imx25_realize;
|
||||
|
||||
/*
|
||||
* Reason: creates an ARM CPU, thus use after free(), see
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo fsl_imx25_type_info = {
|
||||
|
@ -258,6 +258,12 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = fsl_imx31_realize;
|
||||
|
||||
/*
|
||||
* Reason: creates an ARM CPU, thus use after free(), see
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo fsl_imx31_type_info = {
|
||||
|
@ -271,6 +271,12 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
dc->props = xlnx_zynqmp_props;
|
||||
dc->realize = xlnx_zynqmp_realize;
|
||||
|
||||
/*
|
||||
* Reason: creates an ARM CPU, thus use after free(), see
|
||||
* arm_cpu_class_init()
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo xlnx_zynqmp_type_info = {
|
||||
|
@ -500,6 +500,8 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = pci_vpb_reset;
|
||||
dc->vmsd = &pci_vpb_vmstate;
|
||||
dc->props = pci_vpb_properties;
|
||||
/* Reason: object_unref() hangs */
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo pci_vpb_info = {
|
||||
@ -521,10 +523,19 @@ static void pci_realview_init(Object *obj)
|
||||
s->mem_win_size[2] = 0x08000000;
|
||||
}
|
||||
|
||||
static void pci_realview_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(class);
|
||||
|
||||
/* Reason: object_unref() hangs */
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo pci_realview_info = {
|
||||
.name = "realview_pci",
|
||||
.parent = TYPE_VERSATILE_PCI,
|
||||
.instance_init = pci_realview_init,
|
||||
.class_init = pci_realview_class_init,
|
||||
};
|
||||
|
||||
static void versatile_pci_register_types(void)
|
||||
|
@ -114,6 +114,19 @@ typedef struct DeviceClass {
|
||||
* TODO remove once we're there
|
||||
*/
|
||||
bool cannot_instantiate_with_device_add_yet;
|
||||
/*
|
||||
* Does this device model survive object_unref(object_new(TNAME))?
|
||||
* All device models should, and this flag shouldn't exist. Some
|
||||
* devices crash in object_new(), some crash or hang in
|
||||
* object_unref(). Makes introspecting properties with
|
||||
* qmp_device_list_properties() dangerous. Bad, because it's used
|
||||
* by -device FOO,help. This flag serves to protect that code.
|
||||
* It should never be set without a comment explaining why it is
|
||||
* set.
|
||||
* TODO remove once we're there
|
||||
*/
|
||||
bool cannot_destroy_with_object_finalize_yet;
|
||||
|
||||
bool hotpluggable;
|
||||
|
||||
/* callbacks */
|
||||
|
5
qmp.c
5
qmp.c
@ -521,6 +521,11 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (DEVICE_CLASS(klass)->cannot_destroy_with_object_finalize_yet) {
|
||||
error_setg(errp, "Can't list properties of device '%s'", typename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = object_new(typename);
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
||||
|
@ -298,6 +298,13 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data)
|
||||
dc->vmsd = &vmstate_alpha_cpu;
|
||||
#endif
|
||||
cc->gdb_num_core_regs = 67;
|
||||
|
||||
/*
|
||||
* Reason: alpha_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo alpha_cpu_type_info = {
|
||||
|
@ -1427,6 +1427,17 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->debug_excp_handler = arm_debug_excp_handler;
|
||||
|
||||
cc->disas_set_info = arm_disas_set_info;
|
||||
|
||||
/*
|
||||
* Reason: arm_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*
|
||||
* Once this is fixed, the devices that create ARM CPUs should be
|
||||
* updated not to set cannot_destroy_with_object_finalize_yet,
|
||||
* unless they still screw up something else.
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void cpu_register(const ARMCPUInfo *info)
|
||||
|
@ -309,6 +309,13 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->gdb_stop_before_watchpoint = true;
|
||||
|
||||
cc->disas_set_info = cris_disas_set_info;
|
||||
|
||||
/*
|
||||
* Reason: cris_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo cris_cpu_type_info = {
|
||||
|
@ -1453,6 +1453,8 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
|
||||
*/
|
||||
|
||||
dc->props = host_x86_cpu_properties;
|
||||
/* Reason: host_x86_cpu_initfn() dies when !kvm_enabled() */
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void host_x86_cpu_initfn(Object *obj)
|
||||
@ -3190,6 +3192,12 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
|
||||
#endif
|
||||
cc->cpu_exec_enter = x86_cpu_exec_enter;
|
||||
cc->cpu_exec_exit = x86_cpu_exec_exit;
|
||||
|
||||
/*
|
||||
* Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the
|
||||
* object in cpus -> dangling pointer after final object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo x86_cpu_type_info = {
|
||||
|
@ -275,6 +275,13 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->gdb_num_core_regs = 32 + 7;
|
||||
cc->gdb_stop_before_watchpoint = true;
|
||||
cc->debug_excp_handler = lm32_debug_excp_handler;
|
||||
|
||||
/*
|
||||
* Reason: lm32_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void lm32_register_cpu_type(const LM32CPUInfo *info)
|
||||
|
@ -212,6 +212,13 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data)
|
||||
dc->vmsd = &vmstate_m68k_cpu;
|
||||
cc->gdb_num_core_regs = 18;
|
||||
cc->gdb_core_xml_file = "cf-core.xml";
|
||||
|
||||
/*
|
||||
* Reason: m68k_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void register_cpu_type(const M68kCPUInfo *info)
|
||||
|
@ -264,6 +264,12 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->gdb_num_core_regs = 32 + 5;
|
||||
|
||||
cc->disas_set_info = mb_disas_set_info;
|
||||
|
||||
/*
|
||||
* Reason: mb_cpu_initfn() calls cpu_exec_init(), which saves the
|
||||
* object in cpus -> dangling pointer after final object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo mb_cpu_type_info = {
|
||||
|
@ -153,6 +153,13 @@ static void mips_cpu_class_init(ObjectClass *c, void *data)
|
||||
|
||||
cc->gdb_num_core_regs = 73;
|
||||
cc->gdb_stop_before_watchpoint = true;
|
||||
|
||||
/*
|
||||
* Reason: mips_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo mips_cpu_type_info = {
|
||||
|
@ -114,6 +114,13 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->get_phys_page_debug = moxie_cpu_get_phys_page_debug;
|
||||
cc->vmsd = &vmstate_moxie_cpu;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Reason: moxie_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void moxielite_initfn(Object *obj)
|
||||
|
@ -177,6 +177,13 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data)
|
||||
dc->vmsd = &vmstate_openrisc_cpu;
|
||||
#endif
|
||||
cc->gdb_num_core_regs = 32 + 3;
|
||||
|
||||
/*
|
||||
* Reason: openrisc_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void cpu_register(const OpenRISCCPUInfo *info)
|
||||
|
@ -2192,6 +2192,7 @@ static void kvmppc_host_cpu_initfn(Object *obj)
|
||||
|
||||
static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
uint32_t vmx = kvmppc_get_vmx();
|
||||
uint32_t dfp = kvmppc_get_dfp();
|
||||
@ -2218,6 +2219,9 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
|
||||
if (icache_size != -1) {
|
||||
pcc->l1_icache_size = icache_size;
|
||||
}
|
||||
|
||||
/* Reason: kvmppc_host_cpu_initfn() dies when !kvm_enabled() */
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
bool kvmppc_has_cap_epr(void)
|
||||
|
@ -353,6 +353,13 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
|
||||
#endif
|
||||
cc->gdb_num_core_regs = S390_NUM_CORE_REGS;
|
||||
cc->gdb_core_xml_file = "s390x-core64.xml";
|
||||
|
||||
/*
|
||||
* Reason: s390_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo s390_cpu_type_info = {
|
||||
|
@ -290,6 +290,13 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data)
|
||||
#endif
|
||||
dc->vmsd = &vmstate_sh_cpu;
|
||||
cc->gdb_num_core_regs = 59;
|
||||
|
||||
/*
|
||||
* Reason: superh_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo superh_cpu_type_info = {
|
||||
|
@ -854,6 +854,13 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data)
|
||||
#else
|
||||
cc->gdb_num_core_regs = 72;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Reason: sparc_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo sparc_cpu_type_info = {
|
||||
|
@ -159,6 +159,13 @@ static void tilegx_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->set_pc = tilegx_cpu_set_pc;
|
||||
cc->handle_mmu_fault = tilegx_cpu_handle_mmu_fault;
|
||||
cc->gdb_num_core_regs = 0;
|
||||
|
||||
/*
|
||||
* Reason: tilegx_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo tilegx_cpu_type_info = {
|
||||
|
@ -170,6 +170,12 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data)
|
||||
cc->set_pc = tricore_cpu_set_pc;
|
||||
cc->synchronize_from_tb = tricore_cpu_synchronize_from_tb;
|
||||
|
||||
/*
|
||||
* Reason: tricore_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void cpu_register(const TriCoreCPUInfo *info)
|
||||
|
@ -155,6 +155,13 @@ static void uc32_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->get_phys_page_debug = uc32_cpu_get_phys_page_debug;
|
||||
#endif
|
||||
dc->vmsd = &vmstate_uc32_cpu;
|
||||
|
||||
/*
|
||||
* Reason: uc32_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static void uc32_register_cpu_type(const UniCore32CPUInfo *info)
|
||||
|
@ -155,6 +155,13 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data)
|
||||
#endif
|
||||
cc->debug_excp_handler = xtensa_breakpoint_handler;
|
||||
dc->vmsd = &vmstate_xtensa_cpu;
|
||||
|
||||
/*
|
||||
* Reason: xtensa_cpu_initfn() calls cpu_exec_init(), which saves
|
||||
* the object in cpus -> dangling pointer after final
|
||||
* object_unref().
|
||||
*/
|
||||
dc->cannot_destroy_with_object_finalize_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo xtensa_cpu_type_info = {
|
||||
|
@ -91,30 +91,6 @@ static void test_device_intro_abstract(void)
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
static bool blacklisted(const char *type)
|
||||
{
|
||||
static const char *blacklist[] = {
|
||||
/* hang in object_unref(): */
|
||||
"realview_pci", "versatile_pci",
|
||||
/* create a CPU, thus use after free (see below): */
|
||||
"allwinner-a10", "digic", "fsl,imx25", "fsl,imx31", "xlnx,zynqmp",
|
||||
};
|
||||
size_t len = strlen(type);
|
||||
int i;
|
||||
|
||||
if (len >= 4 && !strcmp(type + len - 4, "-cpu")) {
|
||||
/* use after free: cpu_exec_init() saves CPUState in cpus */
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(blacklist); i++) {
|
||||
if (!strcmp(blacklist[i], type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void test_device_intro_concrete(void)
|
||||
{
|
||||
QList *types;
|
||||
@ -128,9 +104,6 @@ static void test_device_intro_concrete(void)
|
||||
type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
|
||||
"name");
|
||||
g_assert(type);
|
||||
if (blacklisted(type)) {
|
||||
continue; /* FIXME broken device, skip */
|
||||
}
|
||||
test_one_device(type);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user