The aux-domain attach/detach are not tracked, some data structures might be used after free. This causes general protection faults when multiple subdevices are created and assigned to a same guest machine: | general protection fault, probably for non-canonical address 0xdead000000000100: 0000 [#1] SMP NOPTI | RIP: 0010:intel_iommu_aux_detach_device+0x12a/0x1f0 | [...] | Call Trace: | iommu_aux_detach_device+0x24/0x70 | vfio_mdev_detach_domain+0x3b/0x60 | ? vfio_mdev_set_domain+0x50/0x50 | iommu_group_for_each_dev+0x4f/0x80 | vfio_iommu_detach_group.isra.0+0x22/0x30 | vfio_iommu_type1_detach_group.cold+0x71/0x211 | ? find_exported_symbol_in_section+0x4a/0xd0 | ? each_symbol_section+0x28/0x50 | __vfio_group_unset_container+0x4d/0x150 | vfio_group_try_dissolve_container+0x25/0x30 | vfio_group_put_external_user+0x13/0x20 | kvm_vfio_group_put_external_user+0x27/0x40 [kvm] | kvm_vfio_destroy+0x45/0xb0 [kvm] | kvm_put_kvm+0x1bb/0x2e0 [kvm] | kvm_vm_release+0x22/0x30 [kvm] | __fput+0xcc/0x260 | ____fput+0xe/0x10 | task_work_run+0x8f/0xb0 | do_exit+0x358/0xaf0 | ? wake_up_state+0x10/0x20 | ? signal_wake_up_state+0x1a/0x30 | do_group_exit+0x47/0xb0 | __x64_sys_exit_group+0x18/0x20 | do_syscall_64+0x57/0x1d0 | entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fix the crash by tracking the subdevices when attaching and detaching aux-domains.
Showing the changes to include/linux (1 of 2 files). Show all
Unified · Split
| @@ -533,11 +533,10 @@ struct dmar_domain { · 532 unmodified lines | |||
| 533 | /* Domain ids per IOMMU. Use u16 since | 533 | /* Domain ids per IOMMU. Use u16 since |
| 534 | * domain ids are 16 bit wide according | 534 | * domain ids are 16 bit wide according |
| 535 | * to VT-d spec, section 9.3 */ | 535 | * to VT-d spec, section 9.3 */ |
| 536 | unsigned int auxd_refcnt; /* Refcount of auxiliary attaching */ | ||
| 537 | 536 | ||
| 538 | bool has_iotlb_device; | 537 | bool has_iotlb_device; |
| 539 | struct list_head devices; /* all devices' list */ | 538 | struct list_head devices; /* all devices' list */ |
| 540 | struct list_head auxd; /* link to device's auxiliary list */ | 539 | struct list_head subdevices; /* all subdevices' list */ |
| 541 | struct iova_domain iovad; /* iova's that belong to this domain */ | 540 | struct iova_domain iovad; /* iova's that belong to this domain */ |
| 542 | 541 | ||
| 543 | struct dma_pte *pgd; /* virtual address */ | 542 | struct dma_pte *pgd; /* virtual address */ |
| @@ -610,14 +609,21 @@ struct intel_iommu { · 66 unmodified lines | |||
| 610 | struct dmar_drhd_unit *drhd; | 609 | struct dmar_drhd_unit *drhd; |
| 611 | }; | 610 | }; |
| 612 | 611 | ||
| 612 | /* Per subdevice private data */ | ||
| 613 | struct subdev_domain_info { | ||
| 614 | struct list_head link_phys; /* link to phys device siblings */ | ||
| 615 | struct list_head link_domain; /* link to domain siblings */ | ||
| 616 | struct device *pdev; /* physical device derived from */ | ||
| 617 | struct dmar_domain *domain; /* aux-domain */ | ||
| 618 | int users; /* user count */ | ||
| 619 | }; | ||
| 620 | |||
| 613 | /* PCI domain-device relationship */ | 621 | /* PCI domain-device relationship */ |
| 614 | struct device_domain_info { | 622 | struct device_domain_info { |
| 615 | struct list_head link; /* link to domain siblings */ | 623 | struct list_head link; /* link to domain siblings */ |
| 616 | struct list_head global; /* link to global list */ | 624 | struct list_head global; /* link to global list */ |
| 617 | struct list_head table; /* link to pasid table */ | 625 | struct list_head table; /* link to pasid table */ |
| 618 | struct list_head auxiliary_domains; /* auxiliary domains | 626 | struct list_head subdevices; /* subdevices sibling */ |
| 619 | * attached to this device | ||
| 620 | */ | ||
| 621 | u32 segment; /* PCI segment number */ | 627 | u32 segment; /* PCI segment number */ |
| 622 | u8 bus; /* PCI bus number */ | 628 | u8 bus; /* PCI bus number */ |
| 623 | u8 devfn; /* PCI devfn number */ | 629 | u8 devfn; /* PCI devfn number */ |