Re: [BUG] iommu: KASAN slab-use-after-free in iommufd_auto_response_faults

From: Peiyang He

Date: Tue Jul 07 2026 - 06:43:42 EST


> The analysis above goes through the iommufd selftest mock device (M). We
> have not reproduced this on real Intel/AMD hardware, but the same lifetime
> issue is not specific to the mock driver: Intel and AMD both have real detach
> paths that can reach the very same iopf_queue_remove_device(). This is exactly
> what iommufd_hwpt_detach_device() triggers, via iommu_detach_group_handle() /
> iommu_detach_device_pasid(), right before it calls iommufd_auto_response_faults().
>
> The following is a static code-path analysis.
>
> iommu_detach_group_handle() (RID-level detach) dispatches through the
> blocking domain's attach_dev op, like mock_domain_nop_attach() in the
> above analysis. iommu_detach_device_pasid() (PASID-level detach) is different:
> it dispatches through the blocking domain's set_dev_pasid op instead
> (drivers/iommu/iommu.c:3815 -> drivers/iommu/iommu.c:3553).
>
> Intel (drivers/iommu/intel/) reaches iopf_queue_remove_device() on both paths:
> blocking_domain_attach_dev() [attach_dev, RID] iommu.c:2755
> blocking_domain_set_dev_pasid() [set_dev_pasid, PASID] iommu.c:3557
> --> iopf_for_domain_remove() iommu.h:1263
> --> intel_iommu_disable_iopf() iommu.c:3471
> --> iopf_queue_remove_device() iommu.c:3483
>
> AMD (drivers/iommu/amd/): the evidence here only covers the RID/group-detach
> path.
> blocked_domain_attach_device() [attach_dev, RID] iommu.c:2883
> --> detach_device() iommu.c:2398
> --> amd_iommu_iopf_remove_device() ppr.c:270
> --> iopf_queue_remove_device() ppr.c:273
> AMD's PASID path, blocked_domain_set_dev_pasid() (iommu.c:2900), only calls
> amd_iommu_remove_dev_pasid() and does not reach iopf_queue_remove_device().

Hello Linux kernel developers and maintainers,

After further analysis of the code, we think that this UAF should not be feasible on AMD devices,
but still feasible on Intel devices.

===
AMD
===

We found no path by which a real AMD PPR fault can land in an IOMMUFD hw_pagetable's
fault->common.deliver or fault->response.

A real AMD PPR fault is always PASID-valid: iommu_call_iopf_notifier(), the only
iommu_report_device_fault() call site under drivers/iommu/amd/, rejects PASID 0 and always sets
IOMMU_FAULT_PAGE_REQUEST_PASID_VALID (drivers/iommu/amd/ppr.c:107,140-148).

find_fault_handler() (drivers/iommu/io-pgfault.c:118) resolves a PASID-valid fault only via a
per-PASID attach_handle, or a fallback to a RID-level NESTED domain gated by ops->user_pasid_table.
AMD does not set that flag (only arm-smmu-v3.c:4389 and the iommufd selftest mock, selftest.c:861,
do), so a failed per-PASID lookup is simply aborted on AMD, never delivered to any RID-level domain.

The per-PASID lookup cannot succeed toward an IOMMUFD HWPT on AMD, because the
AMD domains that IOMMUFD can allocate as HWPTs do not implement .set_dev_pasid:
amdv1_ops (iommu.c:2663), amdv2_ops (iommu.c:2741), and nested_domain_ops
(drivers/iommu/amd/nested.c:291) all lack it. Therefore, if userspace tries to
attach one of those IOMMUFD HWPT domains to a PASID, iommu_attach_device_pasid()
rejects it with -EOPNOTSUPP at the !domain->ops->set_dev_pasid check
(drivers/iommu/iommu.c:3625,3641-3644). The AMD nested RID attach path is not a
substitute here either: nested_attach_device() refuses attachment when PASID is
enabled (WARN_ON(dev_data->pasid_enabled), nested.c:244).

AMD does have a domain with .set_dev_pasid, amd_sva_domain_ops
(drivers/iommu/amd/pasid.c:177), but that is the kernel SVA domain path, not an
IOMMUFD HWPT. The SVA allocation path installs iommu_sva_iopf_handler()
(drivers/iommu/iommu-sva.c:298,327), not iommufd_fault_iopf_handler(), so faults
delivered there are not linked into an iommufd_fault object's deliver/response
queues.

=====
Intel
=====

Intel's PRQ descriptor carries a real, hardware-set pasid_present bit: intel_prq_report()
(drivers/iommu/intel/prq.c:175) only sets IOMMU_FAULT_PAGE_REQUEST_PASID_VALID when
desc->pasid_present is true, so a non-PASID PRI request reaches find_fault_handler()'s RID
(IOMMU_NO_PASID) lookup directly -- a RID-attached fault-capable HWPT can receive it.

Unlike AMD, Intel's IOMMUFD-attachable domains do implement .set_dev_pasid: intel_fs_paging_domain_ops
and intel_ss_paging_domain_ops (drivers/iommu/intel/iommu.c:3894,3905) and intel_nested_domain_ops
(drivers/iommu/intel/nested.c:190) all set it to intel_iommu_set_dev_pasid /
intel_nested_set_dev_pasid, so a PASID-tagged fault can also resolve to an IOMMUFD attach_handle.

Both RID and PASID detach reach iopf_queue_remove_device() the same way as the mock device:
blocking_domain_attach_dev() / blocking_domain_set_dev_pasid() (drivers/iommu/intel/iommu.c:2755,3557)
-> iopf_for_domain_remove() (drivers/iommu/intel/iommu.h:1263) -> intel_iommu_disable_iopf()
(iommu.c:3471) -> iopf_queue_remove_device() (iommu.c:3483), but only once info->iopf_refcount
drops to zero. This refcount is shared per device across every attach path, so the bug may only fire
when the detached domain is the last fault-capable attachment on that device.


Again, the above analysis is based solely on the code.
For now we do not have suitable hardware to test the behavior of the AMD and Intel drivers.

Thanks,
Peiyang