IOMMU Fixes for Linux v7.2-rc5

- AMD:
   * Fix lockdep splat from nested domain allocation
 
   * Fix nested domain leak
 
   * Fix broken synchronisation of command completion
 
   * Fix OOB write in "ivrs_acpihid" command-line parsing
 
 - VT-d:
   * Prevent SVA for IOMMUs with non-coherent page-table walker
 
   * Fix OOB write in PMU driver.
 -----BEGIN PGP SIGNATURE-----
 
 iQFEBAABCgAuFiEEPxTL6PPUbjXGY88ct6xw3ITBYzQFAmpjXsoQHHdpbGxAa2Vy
 bmVsLm9yZwAKCRC3rHDchMFjNGzbB/9xlcRZhOY0aTlqbKglyjyOmqtnJrtNOyUp
 6+gTqiUBcWp9fOXQxA/n+Ne0wYgyYJNgmI0K1Upn0u2ffbnbiT9TLNKEl6fJMW0s
 3g5VNIsqP06SmPVt18eCuLspEP2+JPHvFw7kQY+IAF5odfyXZ4eZ4duk9PPLI5Ov
 ggk4+ePMhFn1pYdK+3MOMqZ/lqc1Q3gWWwvuLnV/FrmQ4dwmh3nE328cbn2yYSBl
 t5e0k+89qBq8ktCO5DbxJQStno757FMmCQ58qcYhpI5oMIbfXl4TYSEsXut88g0e
 jT//iXgyQywLYvbnoCZ5BF5dNI9IZwYolkgwZYaAbM+MN3sZSSwu
 =nAFJ
 -----END PGP SIGNATURE-----

Merge tag 'iommu-fixes-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux

Pull iommu fixes from Will Deacon:
 "Joerg's away at the moment so I've been looking after the IOMMU tree
  in his absence. In the process of doing that, I've hoovered up a
  handful of fixes for the AMD and Intel drivers which address a
  combination of the usual out-of-bounds/locking/leak bugs as well as
  some logical issues around SVA and command completion.

  AMD:

   - Fix lockdep splat from nested domain allocation

   - Fix nested domain leak

   - Fix broken synchronisation of command completion

   - Fix OOB write in "ivrs_acpihid" command-line parsing

  VT-d:

   - Prevent SVA for IOMMUs with non-coherent page-table walker

   - Fix OOB write in PMU driver"

* tag 'iommu-fixes-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux:
  iommu/intel: Fix out-of-bounds memset in dmar_latency_disable()
  iommu/amd: Bound the early ACPI HID map
  iommu/vt-d: Disallow SVA if page walk is not coherent
  iommu/amd: Wait for completion instead of returning early in iommu_completion_wait()
  iommu/amd: Fix nested domain leak
  iommu/amd: Fix IRQ unsafe locking in gdom allocation
This commit is contained in:
Linus Torvalds 2026-07-24 11:47:24 -07:00
commit a93212b3d1
5 changed files with 43 additions and 23 deletions

View File

@ -3864,6 +3864,12 @@ static int __init parse_ivrs_acpihid(char *str)
return 1;
found:
if (early_acpihid_map_size == EARLY_MAP_SIZE) {
pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n",
str);
return 1;
}
p = acpiid;
hid = strsep(&p, ":");
uid = p;

View File

@ -1450,11 +1450,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
int ret;
u64 data;
if (!iommu->need_sync)
return 0;
raw_spin_lock_irqsave(&iommu->lock, flags);
if (!iommu->need_sync) {
/*
* No command has been queued since the last completion-wait.
* A concurrent CPU may have already queued that CWAIT and
* cleared need_sync; need_sync == false only means a covering
* CWAIT is queued, not that all prior commands have completed.
* Wait for the last allocated sequence number so that any
* command queued before this call (possibly on another CPU)
* is guaranteed to have completed before returning.
*/
data = iommu->cmd_sem_val;
raw_spin_unlock_irqrestore(&iommu->lock, flags);
return wait_on_sem(iommu, data);
}
data = get_cmdsem_val(iommu);
build_completion_wait(&cmd, iommu, data);
@ -1464,9 +1476,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
if (ret)
return ret;
ret = wait_on_sem(iommu, data);
return ret;
return wait_on_sem(iommu, data);
}
static void domain_flush_complete(struct protection_domain *domain)

View File

@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte)
return 0;
}
static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index)
static void *gdom_info_load_or_alloc_locked(struct xarray *xa,
unsigned long index,
unsigned long *flags)
{
struct guest_domain_mapping_info *elm, *res;
@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind
if (elm)
return elm;
xa_unlock(xa);
xa_unlock_irqrestore(xa, *flags);
elm = kzalloc_obj(struct guest_domain_mapping_info);
xa_lock(xa);
xa_lock_irqsave(xa, *flags);
if (!elm)
return ERR_PTR(-ENOMEM);
res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC);
if (xa_is_err(res))
res = ERR_PTR(xa_err(res));
@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data)
{
int ret;
unsigned long irqflags;
struct nested_domain *ndom;
struct guest_domain_mapping_info *gdom_info;
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
* keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES
* command must be issued for each hDomID in the xarray.
*/
xa_lock(&aviommu->gdomid_array);
xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id);
gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array,
ndom->gdom_id, &irqflags);
if (IS_ERR(gdom_info)) {
xa_unlock(&aviommu->gdomid_array);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
ret = PTR_ERR(gdom_info);
goto out_err;
}
@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
/* Check if gDomID exist */
if (refcount_inc_not_zero(&gdom_info->users)) {
ndom->gdom_info = gdom_info;
xa_unlock(&aviommu->gdomid_array);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n",
__func__, ndom->gdom_id, gdom_info->hdom_id);
@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
if (gdom_info->hdom_id <= 0) {
__xa_cmpxchg(&aviommu->gdomid_array,
ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC);
xa_unlock(&aviommu->gdomid_array);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
ret = -ENOSPC;
goto out_err_gdom_info;
}
@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
ndom->gdom_info = gdom_info;
refcount_set(&gdom_info->users, 1);
xa_unlock(&aviommu->gdomid_array);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n",
__func__, ndom->gdom_id, gdom_info->hdom_id);
@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
static void nested_domain_free(struct iommu_domain *dom)
{
unsigned long irqflags;
struct guest_domain_mapping_info *curr;
struct nested_domain *ndom = to_ndomain(dom);
struct nested_domain *ndom __free(kfree) = to_ndomain(dom);
struct amd_iommu_viommu *aviommu = ndom->viommu;
xa_lock(&aviommu->gdomid_array);
xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
if (!refcount_dec_and_test(&ndom->gdom_info->users)) {
xa_unlock(&aviommu->gdomid_array);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
return;
}
@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom)
curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id,
ndom->gdom_info, NULL, GFP_ATOMIC);
xa_unlock(&aviommu->gdomid_array);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
if (WARN_ON(!curr || xa_err(curr)))
return;
@ -285,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom)
amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id);
kfree(curr);
kfree(ndom);
}
static const struct iommu_domain_ops nested_domain_ops = {

View File

@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type)
return;
spin_lock_irqsave(&latency_lock, flags);
memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM);
memset(&lstat[type], 0, sizeof(*lstat));
spin_unlock_irqrestore(&latency_lock, flags);
}

View File

@ -27,7 +27,7 @@
void intel_svm_check(struct intel_iommu *iommu)
{
if (!pasid_supported(iommu))
if (!pasid_supported(iommu) || !ecap_smpwc(iommu->ecap))
return;
if (cpu_feature_enabled(X86_FEATURE_GBPAGES) &&