diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index ff249af335..43dc0a12de 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -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 = { diff --git a/hw/arm/digic.c b/hw/arm/digic.c index ec8c330602..90f8190c48 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -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 = { diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 86fde42e34..e1cadac997 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -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 = { diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 8e1ed4811b..53d4473250 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -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 = { diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index a9097f9b72..b36ca3da74 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -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 = { diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 6d23553094..7172b90958 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -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) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 038b54d94b..8057aedaa6 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -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 */ diff --git a/qmp.c b/qmp.c index 1413de439f..d9ecedef93 100644 --- a/qmp.c +++ b/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) { diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c index 421d7e5364..ff1926a5d0 100644 --- a/target-alpha/cpu.c +++ b/target-alpha/cpu.c @@ -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 = { diff --git a/target-arm/cpu.c b/target-arm/cpu.c index d7b4445413..30739fc0df 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -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) diff --git a/target-cris/cpu.c b/target-cris/cpu.c index d461e074c1..8eaf5a5a31 100644 --- a/target-cris/cpu.c +++ b/target-cris/cpu.c @@ -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 = { diff --git a/target-i386/cpu.c b/target-i386/cpu.c index c793812cc2..05d7f26bf1 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -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 = { diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c index c2b77c6986..d0ab2786ae 100644 --- a/target-lm32/cpu.c +++ b/target-lm32/cpu.c @@ -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) diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index 4f246da748..97527ef32a 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -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) diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c index cbd84a22f7..52959e13b4 100644 --- a/target-microblaze/cpu.c +++ b/target-microblaze/cpu.c @@ -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 = { diff --git a/target-mips/cpu.c b/target-mips/cpu.c index 4027d0f417..7fe1f0407f 100644 --- a/target-mips/cpu.c +++ b/target-mips/cpu.c @@ -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 = { diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c index 6b035aaab3..3af37799b7 100644 --- a/target-moxie/cpu.c +++ b/target-moxie/cpu.c @@ -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) diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c index d97f3c03c2..cc5e2d1c5d 100644 --- a/target-openrisc/cpu.c +++ b/target-openrisc/cpu.c @@ -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) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index f8ea783a6d..72762991dc 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -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) diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index c3e21b445c..ccfaa8a919 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -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 = { diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index 5c65ab4df5..64e4467c04 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -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 = { diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index 9528e3afbb..82bb72ab79 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -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 = { diff --git a/target-tilegx/cpu.c b/target-tilegx/cpu.c index 3c5481d443..c24970436d 100644 --- a/target-tilegx/cpu.c +++ b/target-tilegx/cpu.c @@ -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 = { diff --git a/target-tricore/cpu.c b/target-tricore/cpu.c index 2029ef651a..ed8b030ef5 100644 --- a/target-tricore/cpu.c +++ b/target-tricore/cpu.c @@ -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) diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c index fc451a1a35..e5252ebaf8 100644 --- a/target-unicore32/cpu.c +++ b/target-unicore32/cpu.c @@ -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) diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index da8129db50..4e49bee9b5 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -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 = { diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index 8a27b4a702..11d5fea3e2 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -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); }