forked from SLFO-pool/xen
100 lines
3.3 KiB
Diff
100 lines
3.3 KiB
Diff
|
Subject: x86/IOMMU: move tracking in iommu_identity_mapping()
|
||
|
From: Teddy Astie teddy.astie@vates.tech Tue Aug 13 16:36:40 2024 +0200
|
||
|
Date: Tue Aug 13 16:36:40 2024 +0200:
|
||
|
Git: beadd68b5490ada053d72f8a9ce6fd696d626596
|
||
|
|
||
|
If for some reason xmalloc() fails after having mapped the reserved
|
||
|
regions, an error is reported, but the regions remain mapped in the P2M.
|
||
|
|
||
|
Similarly if an error occurs during set_identity_p2m_entry() (except on
|
||
|
the first call), the partial mappings of the region would be retained
|
||
|
without being tracked anywhere, and hence without there being a way to
|
||
|
remove them again from the domain's P2M.
|
||
|
|
||
|
Move the setting up of the list entry ahead of trying to map the region.
|
||
|
In cases other than the first mapping failing, keep record of the full
|
||
|
region, such that a subsequent unmapping request can be properly torn
|
||
|
down.
|
||
|
|
||
|
To compensate for the potentially excess unmapping requests, don't log a
|
||
|
warning from p2m_remove_identity_entry() when there really was nothing
|
||
|
mapped at a given GFN.
|
||
|
|
||
|
This is XSA-460 / CVE-2024-31145.
|
||
|
|
||
|
Fixes: 2201b67b9128 ("VT-d: improve RMRR region handling")
|
||
|
Fixes: c0e19d7c6c42 ("IOMMU: generalize VT-d's tracking of mapped RMRR regions")
|
||
|
Signed-off-by: Teddy Astie <teddy.astie@vates.tech>
|
||
|
Signed-off-by: Jan Beulich <jbeulich@suse.com>
|
||
|
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
|
||
|
|
||
|
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
|
||
|
index e7e327d6a6..1739133fc2 100644
|
||
|
--- a/xen/arch/x86/mm/p2m.c
|
||
|
+++ b/xen/arch/x86/mm/p2m.c
|
||
|
@@ -1267,9 +1267,11 @@ int p2m_remove_identity_entry(struct domain *d, unsigned long gfn_l)
|
||
|
else
|
||
|
{
|
||
|
gfn_unlock(p2m, gfn, 0);
|
||
|
- printk(XENLOG_G_WARNING
|
||
|
- "non-identity map d%d:%lx not cleared (mapped to %lx)\n",
|
||
|
- d->domain_id, gfn_l, mfn_x(mfn));
|
||
|
+ if ( (p2mt != p2m_invalid && p2mt != p2m_mmio_dm) ||
|
||
|
+ a != p2m_access_n || !mfn_eq(mfn, INVALID_MFN) )
|
||
|
+ printk(XENLOG_G_WARNING
|
||
|
+ "non-identity map %pd:%lx not cleared (mapped to %lx)\n",
|
||
|
+ d, gfn_l, mfn_x(mfn));
|
||
|
ret = 0;
|
||
|
}
|
||
|
|
||
|
diff --git a/xen/drivers/passthrough/x86/iommu.c b/xen/drivers/passthrough/x86/iommu.c
|
||
|
index cc0062b027..8b1e0596b8 100644
|
||
|
--- a/xen/drivers/passthrough/x86/iommu.c
|
||
|
+++ b/xen/drivers/passthrough/x86/iommu.c
|
||
|
@@ -267,24 +267,36 @@ int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma,
|
||
|
if ( p2ma == p2m_access_x )
|
||
|
return -ENOENT;
|
||
|
|
||
|
- while ( base_pfn < end_pfn )
|
||
|
- {
|
||
|
- int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag);
|
||
|
-
|
||
|
- if ( err )
|
||
|
- return err;
|
||
|
- base_pfn++;
|
||
|
- }
|
||
|
-
|
||
|
map = xmalloc(struct identity_map);
|
||
|
if ( !map )
|
||
|
return -ENOMEM;
|
||
|
+
|
||
|
map->base = base;
|
||
|
map->end = end;
|
||
|
map->access = p2ma;
|
||
|
map->count = 1;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Insert into list ahead of mapping, so the range can be found when
|
||
|
+ * trying to clean up.
|
||
|
+ */
|
||
|
list_add_tail(&map->list, &hd->arch.identity_maps);
|
||
|
|
||
|
+ for ( ; base_pfn < end_pfn; ++base_pfn )
|
||
|
+ {
|
||
|
+ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag);
|
||
|
+
|
||
|
+ if ( !err )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ( (map->base >> PAGE_SHIFT_4K) == base_pfn )
|
||
|
+ {
|
||
|
+ list_del(&map->list);
|
||
|
+ xfree(map);
|
||
|
+ }
|
||
|
+ return err;
|
||
|
+ }
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|